From 3d0dc2535162dbd7f39e037561bc86aeafa568e2 Mon Sep 17 00:00:00 2001 From: locnguyen1986 Date: Sun, 8 Feb 2026 11:35:14 +0700 Subject: [PATCH 1/4] clean code platforms --- .../workflows/app-platform-release-prod.yml | 20 - .github/workflows/ci-app-platform-dev.yml | 29 - .github/workflows/ci-production-release.yml | 13 - .../deprecated-ci-app-platform-prod.yml | 27 - AGENTS.md | 55 +- CLAUDE.md | 8 +- Makefile | 38 +- README.md | 8 +- apps/platform/.dockerignore | 16 - apps/platform/.env.example | 1 - apps/platform/.gitignore | 44 - apps/platform/.prettierrc.json | 16 - apps/platform/.source/index.ts | 89 - apps/platform/.source/source.config.mjs | 30 - apps/platform/.yarnrc.yml | 1 - apps/platform/Dockerfile | 61 - apps/platform/api/server.json | 10089 ---------------- apps/platform/api/server.yaml | 6769 ----------- apps/platform/cli.json | 11 - apps/platform/components.json | 22 - .../api-reference/admin-model-api/meta.json | 11 - .../models/catalogs/bulk-toggle/post.mdx | 23 - .../admin-model-api/models/catalogs/get.mdx | 21 - .../admin-model-api/models/catalogs/patch.mdx | 21 - .../provider-models/bulk-toggle/post.mdx | 23 - .../models/provider-models/get.mdx | 17 - .../models/provider-models/patch.mdx | 21 - .../admin-provider-api/meta.json | 4 - .../admin-provider-api/providers/get.mdx | 17 - .../admin-provider-api/providers/patch.mdx | 17 - .../admin-provider-api/providers/post.mdx | 17 - .../authentication-api/api-keys/delete.mdx | 21 - .../authentication-api/api-keys/get.mdx | 21 - .../authentication-api/api-keys/post.mdx | 21 - .../authentication-api/callback/get.mdx | 21 - .../authentication-api/guest-login/post.mdx | 21 - .../authentication-api/login/get.mdx | 21 - .../authentication-api/logout/get.mdx | 21 - .../authentication-api/me/get.mdx | 21 - .../authentication-api/meta.json | 18 - .../authentication-api/refresh-token/post.mdx | 21 - .../authentication-api/revoke/post.mdx | 17 - .../authentication-api/upgrade/post.mdx | 22 - .../validate-api-key/post.mdx | 21 - .../authentication-api/validate/post.mdx | 17 - .../api-reference/backward-compatibility.mdx | 33 - .../api-reference/chat-completions-api.mdx | 31 - .../chat-completions-api/completions/post.mdx | 99 - .../chat-completions-api/meta.json | 4 - .../chat-completions-api/models/get.mdx | 23 - .../conv-public-id/items/delete.mdx | 75 - .../conv-public-id/items/get.mdx | 47 - .../conv-public-id/items/post.mdx | 53 - .../conversations/delete.mdx | 61 - .../conversations-api/conversations/get.mdx | 61 - .../conversations-api/conversations/post.mdx | 61 - .../api-reference/conversations-api/meta.json | 11 - .../docs/api-reference/debugging-requests.mdx | 561 - .../docs/api-reference/introduction.mdx | 446 - .../content/docs/api-reference/meta.json | 18 - .../docs/api-reference/model-api/meta.json | 4 - .../model-api/models/catalogs/get.mdx | 21 - .../model-api/models/providers/get.mdx | 19 - .../docs/api-reference/projects-api/meta.json | 4 - .../projects-api/projects/delete.mdx | 17 - .../projects-api/projects/get.mdx | 17 - .../projects-api/projects/patch.mdx | 17 - .../projects-api/projects/post.mdx | 17 - .../api-reference/server-api/healthz/get.mdx | 21 - .../docs/api-reference/server-api/meta.json | 4 - .../api-reference/server-api/readyz/get.mdx | 21 - .../api-reference/server-api/version/get.mdx | 21 - .../content/docs/architecture/data-flow.mdx | 50 - .../content/docs/architecture/index.mdx | 96 - .../content/docs/architecture/meta.json | 13 - .../docs/architecture/observability.mdx | 53 - .../docs/architecture/security-advanced.mdx | 237 - .../content/docs/architecture/security.mdx | 46 - .../content/docs/architecture/services.mdx | 92 - .../docs/architecture/system-design.mdx | 131 - .../content/docs/architecture/test-flows.mdx | 679 -- .../docs/configuration/docker-compose.mdx | 86 - .../docs/configuration/env-var-mapping.mdx | 310 - .../content/docs/configuration/index.mdx | 398 - .../content/docs/configuration/kubernetes.mdx | 333 - .../content/docs/configuration/meta.json | 11 - .../content/docs/configuration/precedence.mdx | 502 - .../docs/configuration/service-migration.mdx | 169 - .../conventions/architecture-patterns.mdx | 140 - .../content/docs/conventions/conventions.mdx | 103 - .../docs/conventions/design-patterns.mdx | 646 - .../content/docs/conventions/meta.json | 4 - .../content/docs/conventions/workflow.mdx | 209 - .../content/docs/guides/authentication.mdx | 87 - .../content/docs/guides/background-mode.mdx | 423 - .../docs/guides/conversation-management.mdx | 471 - .../content/docs/guides/deployment.mdx | 732 -- .../content/docs/guides/development.mdx | 352 - apps/platform/content/docs/guides/index.mdx | 121 - apps/platform/content/docs/guides/jan-cli.mdx | 798 -- .../content/docs/guides/kong-plugins.mdx | 279 - .../docs/guides/mcp-admin-interface.mdx | 375 - .../content/docs/guides/mcp-testing.mdx | 128 - apps/platform/content/docs/guides/meta.json | 24 - .../docs/guides/monitoring-advanced.mdx | 529 - .../content/docs/guides/monitoring.mdx | 358 - .../docs/guides/prompt-orchestration.mdx | 546 - .../content/docs/guides/services-template.mdx | 25 - apps/platform/content/docs/guides/testing.mdx | 510 - .../content/docs/guides/troubleshooting.mdx | 473 - .../docs/guides/user-management-todo.mdx | 850 -- .../guides/user-settings-personalization.mdx | 185 - .../platform/content/docs/guides/webhooks.mdx | 864 -- apps/platform/content/docs/meta.json | 14 - apps/platform/content/docs/quickstart.mdx | 351 - apps/platform/content/docs/repo-naming.mdx | 423 - apps/platform/content/docs/roadmap.mdx | 503 - apps/platform/content/docs/runbooks/index.mdx | 20 - apps/platform/content/docs/runbooks/meta.json | 4 - .../content/docs/runbooks/monitoring.mdx | 406 - apps/platform/eslint.config.mjs | 19 - apps/platform/next.config.mjs | 23 - apps/platform/package.json | 65 - apps/platform/postcss.config.mjs | 5 - apps/platform/public/favicon.ico | Bin 15406 -> 0 bytes apps/platform/public/jan_logo.svg | 14 - apps/platform/scripts/README.md | 185 - .../scripts/generate-fumadocs-openapi.ts | 326 - apps/platform/scripts/setup-env.bat | 88 - apps/platform/scripts/setup-env.sh | 94 - apps/platform/scripts/test-import.ts | 1 - apps/platform/source.config.mjs | 1 - apps/platform/source.config.ts | 30 - apps/platform/src/app/(home)/layout.tsx | 6 - apps/platform/src/app/(home)/page.tsx | 93 - apps/platform/src/app/(private)/layout.tsx | 16 - apps/platform/src/app/admin/layout.tsx | 170 - .../mcp-tools/components/mcp-tool-modal.tsx | 230 - .../platform/src/app/admin/mcp-tools/page.tsx | 244 - .../components/ModelPromptTemplatesTab.tsx | 369 - .../src/app/admin/models/catalogs/page.tsx | 1895 --- apps/platform/src/app/admin/models/page.tsx | 255 - .../app/admin/models/provider-models/page.tsx | 768 -- .../src/app/admin/models/providers/page.tsx | 997 -- apps/platform/src/app/admin/page.tsx | 243 - .../components/preview-modal.tsx | 116 - .../components/prompt-template-modal.tsx | 357 - .../src/app/admin/prompt-templates/page.tsx | 362 - .../app/admin/users/feature-flags/page.tsx | 562 - apps/platform/src/app/admin/users/page.tsx | 1227 -- apps/platform/src/app/api/search/route.ts | 7 - .../src/app/auth/[provider]/callback/page.tsx | 43 - .../src/app/docs/[[...slug]]/page.tsx | 64 - apps/platform/src/app/docs/docs-fix.css | 11 - apps/platform/src/app/docs/layout.tsx | 30 - apps/platform/src/app/global.css | 168 - apps/platform/src/app/layout.tsx | 32 - apps/platform/src/app/llms-full.txt/route.ts | 10 - .../src/app/og/docs/[...slug]/route.tsx | 31 - .../platform/src/app/profile/api-keys-tab.tsx | 344 - apps/platform/src/app/profile/page.tsx | 48 - apps/platform/src/app/profile/user-tab.tsx | 48 - apps/platform/src/app/sitemap.ts | 24 - .../platform/src/components/auth-provider.tsx | 96 - apps/platform/src/components/coming-soon.tsx | 35 - .../landing/code-example-client.tsx | 74 - .../src/components/landing/code-example.tsx | 66 - .../src/components/landing/feature-card.tsx | 23 - apps/platform/src/components/landing/hero.tsx | 18 - .../src/components/landing/landing-page.tsx | 126 - .../src/components/landing/model-card.tsx | 21 - apps/platform/src/components/login-form.tsx | 35 - apps/platform/src/components/navbar.tsx | 230 - apps/platform/src/components/otp-form.tsx | 142 - apps/platform/src/components/site-header.tsx | 124 - apps/platform/src/components/theme-toggle.tsx | 41 - apps/platform/src/components/ui/avatar.tsx | 41 - apps/platform/src/components/ui/badge.tsx | 39 - apps/platform/src/components/ui/button.tsx | 58 - apps/platform/src/components/ui/card.tsx | 75 - apps/platform/src/components/ui/dialog.tsx | 129 - .../src/components/ui/dropdown-menu.tsx | 228 - apps/platform/src/components/ui/empty.tsx | 94 - apps/platform/src/components/ui/field.tsx | 234 - apps/platform/src/components/ui/form.tsx | 169 - apps/platform/src/components/ui/input-otp.tsx | 70 - apps/platform/src/components/ui/input.tsx | 21 - apps/platform/src/components/ui/label.tsx | 21 - apps/platform/src/components/ui/select.tsx | 172 - apps/platform/src/components/ui/separator.tsx | 28 - apps/platform/src/components/ui/skeleton.tsx | 13 - apps/platform/src/components/ui/table.tsx | 92 - apps/platform/src/lib/admin/api.ts | 1138 -- apps/platform/src/lib/auth/api.ts | 141 - apps/platform/src/lib/auth/broadcast.ts | 172 - apps/platform/src/lib/auth/const.ts | 39 - apps/platform/src/lib/auth/errors.ts | 17 - apps/platform/src/lib/auth/providers/api.ts | 97 - apps/platform/src/lib/auth/providers/base.ts | 52 - apps/platform/src/lib/auth/providers/index.ts | 19 - .../src/lib/auth/providers/keycloak.ts | 39 - apps/platform/src/lib/auth/providers/types.ts | 34 - apps/platform/src/lib/auth/registry.ts | 25 - apps/platform/src/lib/auth/service.ts | 611 - apps/platform/src/lib/auth/types.ts | 34 - apps/platform/src/lib/dummy.ts | 7 - apps/platform/src/lib/layout.shared.tsx | 16 - apps/platform/src/lib/openapi.ts | 6 - apps/platform/src/lib/source.ts | 28 - apps/platform/src/lib/utils.ts | 6 - apps/platform/src/mdx-components.tsx | 13 - apps/platform/src/proxy.ts | 45 - apps/platform/src/store/auth-store.ts | 69 - apps/platform/src/store/project-store.ts | 99 - apps/platform/tsconfig.json | 38 - docker-compose.yml | 1 - docs/guides/admin-feature.md | 1171 -- docs/repo-naming.md | 1 - infra/docker/apps-platform.yml | 30 - tools/jan-cli/cmd_docs.go | 376 - tsconfig.json | 1 - 221 files changed, 11 insertions(+), 50199 deletions(-) delete mode 100644 .github/workflows/app-platform-release-prod.yml delete mode 100644 .github/workflows/ci-app-platform-dev.yml delete mode 100644 .github/workflows/deprecated-ci-app-platform-prod.yml delete mode 100644 apps/platform/.dockerignore delete mode 100644 apps/platform/.env.example delete mode 100644 apps/platform/.gitignore delete mode 100644 apps/platform/.prettierrc.json delete mode 100644 apps/platform/.source/index.ts delete mode 100644 apps/platform/.source/source.config.mjs delete mode 100644 apps/platform/.yarnrc.yml delete mode 100644 apps/platform/Dockerfile delete mode 100644 apps/platform/api/server.json delete mode 100644 apps/platform/api/server.yaml delete mode 100644 apps/platform/cli.json delete mode 100644 apps/platform/components.json delete mode 100644 apps/platform/content/docs/api-reference/admin-model-api/meta.json delete mode 100644 apps/platform/content/docs/api-reference/admin-model-api/models/catalogs/bulk-toggle/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/admin-model-api/models/catalogs/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/admin-model-api/models/catalogs/patch.mdx delete mode 100644 apps/platform/content/docs/api-reference/admin-model-api/models/provider-models/bulk-toggle/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/admin-model-api/models/provider-models/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/admin-model-api/models/provider-models/patch.mdx delete mode 100644 apps/platform/content/docs/api-reference/admin-provider-api/meta.json delete mode 100644 apps/platform/content/docs/api-reference/admin-provider-api/providers/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/admin-provider-api/providers/patch.mdx delete mode 100644 apps/platform/content/docs/api-reference/admin-provider-api/providers/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/api-keys/delete.mdx delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/api-keys/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/api-keys/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/callback/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/guest-login/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/login/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/logout/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/me/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/meta.json delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/refresh-token/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/revoke/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/upgrade/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/validate-api-key/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/authentication-api/validate/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/backward-compatibility.mdx delete mode 100644 apps/platform/content/docs/api-reference/chat-completions-api.mdx delete mode 100644 apps/platform/content/docs/api-reference/chat-completions-api/completions/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/chat-completions-api/meta.json delete mode 100644 apps/platform/content/docs/api-reference/chat-completions-api/models/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/conversations-api/conv-public-id/items/delete.mdx delete mode 100644 apps/platform/content/docs/api-reference/conversations-api/conv-public-id/items/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/conversations-api/conv-public-id/items/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/conversations-api/conversations/delete.mdx delete mode 100644 apps/platform/content/docs/api-reference/conversations-api/conversations/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/conversations-api/conversations/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/conversations-api/meta.json delete mode 100644 apps/platform/content/docs/api-reference/debugging-requests.mdx delete mode 100644 apps/platform/content/docs/api-reference/introduction.mdx delete mode 100644 apps/platform/content/docs/api-reference/meta.json delete mode 100644 apps/platform/content/docs/api-reference/model-api/meta.json delete mode 100644 apps/platform/content/docs/api-reference/model-api/models/catalogs/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/model-api/models/providers/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/projects-api/meta.json delete mode 100644 apps/platform/content/docs/api-reference/projects-api/projects/delete.mdx delete mode 100644 apps/platform/content/docs/api-reference/projects-api/projects/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/projects-api/projects/patch.mdx delete mode 100644 apps/platform/content/docs/api-reference/projects-api/projects/post.mdx delete mode 100644 apps/platform/content/docs/api-reference/server-api/healthz/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/server-api/meta.json delete mode 100644 apps/platform/content/docs/api-reference/server-api/readyz/get.mdx delete mode 100644 apps/platform/content/docs/api-reference/server-api/version/get.mdx delete mode 100644 apps/platform/content/docs/architecture/data-flow.mdx delete mode 100644 apps/platform/content/docs/architecture/index.mdx delete mode 100644 apps/platform/content/docs/architecture/meta.json delete mode 100644 apps/platform/content/docs/architecture/observability.mdx delete mode 100644 apps/platform/content/docs/architecture/security-advanced.mdx delete mode 100644 apps/platform/content/docs/architecture/security.mdx delete mode 100644 apps/platform/content/docs/architecture/services.mdx delete mode 100644 apps/platform/content/docs/architecture/system-design.mdx delete mode 100644 apps/platform/content/docs/architecture/test-flows.mdx delete mode 100644 apps/platform/content/docs/configuration/docker-compose.mdx delete mode 100644 apps/platform/content/docs/configuration/env-var-mapping.mdx delete mode 100644 apps/platform/content/docs/configuration/index.mdx delete mode 100644 apps/platform/content/docs/configuration/kubernetes.mdx delete mode 100644 apps/platform/content/docs/configuration/meta.json delete mode 100644 apps/platform/content/docs/configuration/precedence.mdx delete mode 100644 apps/platform/content/docs/configuration/service-migration.mdx delete mode 100644 apps/platform/content/docs/conventions/architecture-patterns.mdx delete mode 100644 apps/platform/content/docs/conventions/conventions.mdx delete mode 100644 apps/platform/content/docs/conventions/design-patterns.mdx delete mode 100644 apps/platform/content/docs/conventions/meta.json delete mode 100644 apps/platform/content/docs/conventions/workflow.mdx delete mode 100644 apps/platform/content/docs/guides/authentication.mdx delete mode 100644 apps/platform/content/docs/guides/background-mode.mdx delete mode 100644 apps/platform/content/docs/guides/conversation-management.mdx delete mode 100644 apps/platform/content/docs/guides/deployment.mdx delete mode 100644 apps/platform/content/docs/guides/development.mdx delete mode 100644 apps/platform/content/docs/guides/index.mdx delete mode 100644 apps/platform/content/docs/guides/jan-cli.mdx delete mode 100644 apps/platform/content/docs/guides/kong-plugins.mdx delete mode 100644 apps/platform/content/docs/guides/mcp-admin-interface.mdx delete mode 100644 apps/platform/content/docs/guides/mcp-testing.mdx delete mode 100644 apps/platform/content/docs/guides/meta.json delete mode 100644 apps/platform/content/docs/guides/monitoring-advanced.mdx delete mode 100644 apps/platform/content/docs/guides/monitoring.mdx delete mode 100644 apps/platform/content/docs/guides/prompt-orchestration.mdx delete mode 100644 apps/platform/content/docs/guides/services-template.mdx delete mode 100644 apps/platform/content/docs/guides/testing.mdx delete mode 100644 apps/platform/content/docs/guides/troubleshooting.mdx delete mode 100644 apps/platform/content/docs/guides/user-management-todo.mdx delete mode 100644 apps/platform/content/docs/guides/user-settings-personalization.mdx delete mode 100644 apps/platform/content/docs/guides/webhooks.mdx delete mode 100644 apps/platform/content/docs/meta.json delete mode 100644 apps/platform/content/docs/quickstart.mdx delete mode 100644 apps/platform/content/docs/repo-naming.mdx delete mode 100644 apps/platform/content/docs/roadmap.mdx delete mode 100644 apps/platform/content/docs/runbooks/index.mdx delete mode 100644 apps/platform/content/docs/runbooks/meta.json delete mode 100644 apps/platform/content/docs/runbooks/monitoring.mdx delete mode 100644 apps/platform/eslint.config.mjs delete mode 100644 apps/platform/next.config.mjs delete mode 100644 apps/platform/package.json delete mode 100644 apps/platform/postcss.config.mjs delete mode 100644 apps/platform/public/favicon.ico delete mode 100644 apps/platform/public/jan_logo.svg delete mode 100644 apps/platform/scripts/README.md delete mode 100644 apps/platform/scripts/generate-fumadocs-openapi.ts delete mode 100644 apps/platform/scripts/setup-env.bat delete mode 100644 apps/platform/scripts/setup-env.sh delete mode 100644 apps/platform/scripts/test-import.ts delete mode 100644 apps/platform/source.config.mjs delete mode 100644 apps/platform/source.config.ts delete mode 100644 apps/platform/src/app/(home)/layout.tsx delete mode 100644 apps/platform/src/app/(home)/page.tsx delete mode 100644 apps/platform/src/app/(private)/layout.tsx delete mode 100644 apps/platform/src/app/admin/layout.tsx delete mode 100644 apps/platform/src/app/admin/mcp-tools/components/mcp-tool-modal.tsx delete mode 100644 apps/platform/src/app/admin/mcp-tools/page.tsx delete mode 100644 apps/platform/src/app/admin/models/catalogs/components/ModelPromptTemplatesTab.tsx delete mode 100644 apps/platform/src/app/admin/models/catalogs/page.tsx delete mode 100644 apps/platform/src/app/admin/models/page.tsx delete mode 100644 apps/platform/src/app/admin/models/provider-models/page.tsx delete mode 100644 apps/platform/src/app/admin/models/providers/page.tsx delete mode 100644 apps/platform/src/app/admin/page.tsx delete mode 100644 apps/platform/src/app/admin/prompt-templates/components/preview-modal.tsx delete mode 100644 apps/platform/src/app/admin/prompt-templates/components/prompt-template-modal.tsx delete mode 100644 apps/platform/src/app/admin/prompt-templates/page.tsx delete mode 100644 apps/platform/src/app/admin/users/feature-flags/page.tsx delete mode 100644 apps/platform/src/app/admin/users/page.tsx delete mode 100644 apps/platform/src/app/api/search/route.ts delete mode 100644 apps/platform/src/app/auth/[provider]/callback/page.tsx delete mode 100644 apps/platform/src/app/docs/[[...slug]]/page.tsx delete mode 100644 apps/platform/src/app/docs/docs-fix.css delete mode 100644 apps/platform/src/app/docs/layout.tsx delete mode 100644 apps/platform/src/app/global.css delete mode 100644 apps/platform/src/app/layout.tsx delete mode 100644 apps/platform/src/app/llms-full.txt/route.ts delete mode 100644 apps/platform/src/app/og/docs/[...slug]/route.tsx delete mode 100644 apps/platform/src/app/profile/api-keys-tab.tsx delete mode 100644 apps/platform/src/app/profile/page.tsx delete mode 100644 apps/platform/src/app/profile/user-tab.tsx delete mode 100644 apps/platform/src/app/sitemap.ts delete mode 100644 apps/platform/src/components/auth-provider.tsx delete mode 100644 apps/platform/src/components/coming-soon.tsx delete mode 100644 apps/platform/src/components/landing/code-example-client.tsx delete mode 100644 apps/platform/src/components/landing/code-example.tsx delete mode 100644 apps/platform/src/components/landing/feature-card.tsx delete mode 100644 apps/platform/src/components/landing/hero.tsx delete mode 100644 apps/platform/src/components/landing/landing-page.tsx delete mode 100644 apps/platform/src/components/landing/model-card.tsx delete mode 100644 apps/platform/src/components/login-form.tsx delete mode 100644 apps/platform/src/components/navbar.tsx delete mode 100644 apps/platform/src/components/otp-form.tsx delete mode 100644 apps/platform/src/components/site-header.tsx delete mode 100644 apps/platform/src/components/theme-toggle.tsx delete mode 100644 apps/platform/src/components/ui/avatar.tsx delete mode 100644 apps/platform/src/components/ui/badge.tsx delete mode 100644 apps/platform/src/components/ui/button.tsx delete mode 100644 apps/platform/src/components/ui/card.tsx delete mode 100644 apps/platform/src/components/ui/dialog.tsx delete mode 100644 apps/platform/src/components/ui/dropdown-menu.tsx delete mode 100644 apps/platform/src/components/ui/empty.tsx delete mode 100644 apps/platform/src/components/ui/field.tsx delete mode 100644 apps/platform/src/components/ui/form.tsx delete mode 100644 apps/platform/src/components/ui/input-otp.tsx delete mode 100644 apps/platform/src/components/ui/input.tsx delete mode 100644 apps/platform/src/components/ui/label.tsx delete mode 100644 apps/platform/src/components/ui/select.tsx delete mode 100644 apps/platform/src/components/ui/separator.tsx delete mode 100644 apps/platform/src/components/ui/skeleton.tsx delete mode 100644 apps/platform/src/components/ui/table.tsx delete mode 100644 apps/platform/src/lib/admin/api.ts delete mode 100644 apps/platform/src/lib/auth/api.ts delete mode 100644 apps/platform/src/lib/auth/broadcast.ts delete mode 100644 apps/platform/src/lib/auth/const.ts delete mode 100644 apps/platform/src/lib/auth/errors.ts delete mode 100644 apps/platform/src/lib/auth/providers/api.ts delete mode 100644 apps/platform/src/lib/auth/providers/base.ts delete mode 100644 apps/platform/src/lib/auth/providers/index.ts delete mode 100644 apps/platform/src/lib/auth/providers/keycloak.ts delete mode 100644 apps/platform/src/lib/auth/providers/types.ts delete mode 100644 apps/platform/src/lib/auth/registry.ts delete mode 100644 apps/platform/src/lib/auth/service.ts delete mode 100644 apps/platform/src/lib/auth/types.ts delete mode 100644 apps/platform/src/lib/dummy.ts delete mode 100644 apps/platform/src/lib/layout.shared.tsx delete mode 100644 apps/platform/src/lib/openapi.ts delete mode 100644 apps/platform/src/lib/source.ts delete mode 100644 apps/platform/src/lib/utils.ts delete mode 100644 apps/platform/src/mdx-components.tsx delete mode 100644 apps/platform/src/proxy.ts delete mode 100644 apps/platform/src/store/auth-store.ts delete mode 100644 apps/platform/src/store/project-store.ts delete mode 100644 apps/platform/tsconfig.json delete mode 100644 docs/guides/admin-feature.md delete mode 100644 infra/docker/apps-platform.yml delete mode 100644 tools/jan-cli/cmd_docs.go diff --git a/.github/workflows/app-platform-release-prod.yml b/.github/workflows/app-platform-release-prod.yml deleted file mode 100644 index 3ebe0627..00000000 --- a/.github/workflows/app-platform-release-prod.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: CI - App Platform Release Prod - -on: - push: - tags: - - "v[0-9]+.[0-9]+.[0-9]+.[0-9][0-9]3" - -jobs: - build-platform: - uses: ./.github/workflows/_reusable-docker.yml - secrets: inherit - with: - runs-on: ubuntu-24-04-docker - docker-file: apps/platform/Dockerfile - context: . - registry-url: registry.menlo.ai - tags: registry.menlo.ai/menlo-platform/platform-web:prod-${{ github.ref_name }} - is_push: true - build-args: | - NEXT_PUBLIC_JAN_BASE_URL=https://api.jan.ai \ No newline at end of file diff --git a/.github/workflows/ci-app-platform-dev.yml b/.github/workflows/ci-app-platform-dev.yml deleted file mode 100644 index 961d31a7..00000000 --- a/.github/workflows/ci-app-platform-dev.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: CI - Platform App (Dev) - -on: - push: - branches: - - main - paths: - - "apps/platform/**" - - ".github/workflows/ci-app-platform-dev.yml" - pull_request: - branches: - - main - paths: - - "apps/platform/**" - - ".github/workflows/ci-app-platform-dev.yml" - -jobs: - build-docker-x64: - uses: ./.github/workflows/_reusable-docker.yml - secrets: inherit - with: - runs-on: ubuntu-24-04-docker - docker-file: apps/platform/Dockerfile - context: . - registry-url: registry.menlo.ai - tags: registry.menlo.ai/menlo-platform/platform-web:dev-${{ github.sha }} - build-args: | - NEXT_PUBLIC_JAN_BASE_URL=https://api-dev.jan.ai - is_push: ${{ github.event_name == 'push' }} diff --git a/.github/workflows/ci-production-release.yml b/.github/workflows/ci-production-release.yml index 66b9e8d1..a093fb59 100644 --- a/.github/workflows/ci-production-release.yml +++ b/.github/workflows/ci-production-release.yml @@ -108,19 +108,6 @@ jobs: build-contexts: | go-common=packages/go-common - build-platform: - uses: ./.github/workflows/_reusable-docker.yml - secrets: inherit - with: - runs-on: ubuntu-24-04-docker - docker-file: apps/platform/Dockerfile - context: . - registry-url: registry.menlo.ai - tags: registry.menlo.ai/menlo-platform/platform-web:prod-${{ github.ref_name }} - is_push: true - build-args: | - NEXT_PUBLIC_JAN_BASE_URL=https://api.jan.ai - build-web: runs-on: ubuntu-latest permissions: diff --git a/.github/workflows/deprecated-ci-app-platform-prod.yml b/.github/workflows/deprecated-ci-app-platform-prod.yml deleted file mode 100644 index c15e44f4..00000000 --- a/.github/workflows/deprecated-ci-app-platform-prod.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: CI - Platform App (Prod) - -on: - push: - branches: - - release - paths: - - "apps/platform/**" - - "packages/shared-ui/**" - - "packages/shared-types/**" - - "packages/shared-utils/**" - tags: - - "v[0-9]+.[0-9]+.[0-9]+" - -jobs: - build-docker-x64: - uses: ./.github/workflows/_reusable-docker.yml - secrets: inherit - with: - runs-on: ubuntu-24-04-docker - docker-file: apps/platform/Dockerfile - context: apps/platform - registry-url: registry.menlo.ai - tags: registry.menlo.ai/server/platform:prod-${{ github.ref_name }} - build-args: | - NEXT_PUBLIC_JAN_BASE_URL=https://api.jan.ai - is_push: ${{ github.event_name == 'push' }} diff --git a/AGENTS.md b/AGENTS.md index 96959605..9b06732d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -22,7 +22,6 @@ | Container | Docker Compose (dev), Kubernetes/Helm (prod) | | **Frontend** | | | Web App | React 19 + Vite + TanStack Router + Tailwind CSS 4 | -| Platform App | Next.js 16 + Fumadocs (docs) + Tailwind CSS 4 | | UI Components | Radix UI + shadcn/ui patterns | | State | Zustand | | AI SDK | Vercel AI SDK (@ai-sdk/react) | @@ -150,56 +149,9 @@ npm run build # Production build npm run lint # ESLint ``` -### Platform App (`apps/platform/`) - -Admin panel and documentation site built with Next.js 16. - -``` -apps/platform/ -├── src/ -│ ├── app/ # Next.js App Router -│ │ ├── admin/ # Admin pages (users, models, MCP tools) -│ │ ├── docs/ # Fumadocs documentation -│ │ ├── auth/ # Authentication pages -│ │ └── api/ # API routes -│ ├── components/ # Shared components -│ ├── lib/ # Utilities -│ └── store/ # Zustand stores -├── content/ # MDX documentation content -├── api/ # OpenAPI specs for docs -├── package.json -└── next.config.mjs -``` - -**Key Technologies:** -- **Framework:** Next.js 16 with App Router + Turbopack -- **Docs:** Fumadocs (MDX-based documentation) -- **State:** Zustand -- **UI:** Radix UI + Tailwind CSS 4 + Lucide icons - -**Admin Features:** -- User management -- Model/Provider configuration -- MCP Tools management (descriptions, active status, keyword filters) -- Prompt templates - -**Environment Variables:** -```env -NEXT_PUBLIC_JAN_BASE_URL=http://localhost:8000 # Kong gateway URL -``` - -**Commands:** -```bash -cd apps/platform -npm install -npm run dev # Start dev server on port 3000 -npm run build # Production build -npm run generate-openapi # Generate API docs from OpenAPI specs -``` - ### Shared Package (`packages/interfaces/`) -Shared UI components library used by both apps. +Shared UI components library used by the web app. ``` packages/interfaces/ @@ -225,15 +177,12 @@ import { cn } from "@janhq/interfaces/lib" # Start full stack (backend + frontend) make up-full make up-web # Start web app container -make up-platform # Start platform app container -# Or run frontends locally +# Or run frontend locally cd apps/web && npm run dev # http://localhost:3001 -cd apps/platform && npm run dev # http://localhost:3000 # Build for production cd apps/web && npm run build -cd apps/platform && npm run build ``` --- diff --git a/CLAUDE.md b/CLAUDE.md index d5a46f18..c2578695 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -56,7 +56,6 @@ make down-clean # Stop containers and remove volumes | Component | Technology | Version | |------------------|----------------------|-----------| | Web App | React + Vite | 19.2.0 | -| Platform App | Next.js | 16.0.7 | | Router | TanStack Router | v1.140.0 | | Styling | Tailwind CSS | 4.1.17 | | UI Components | Radix UI + shadcn/ui | Latest | @@ -71,8 +70,7 @@ make down-clean # Stop containers and remove volumes ``` jan-server/ ├── apps/ # Frontend applications -│ ├── web/ # Chat UI (React + Vite, port 3001) -│ └── platform/ # Admin & docs site (Next.js, port 3000) +│ └── web/ # Chat UI (React + Vite, port 3001) ├── packages/ # Shared packages │ ├── interfaces/ # Shared UI components (@janhq/interfaces) │ └── go-common/ # Shared Go utilities (config, errors, observability) @@ -340,13 +338,11 @@ go test ./services//... # Unit tests for specific service ### Frontend Development ```bash -# Run frontend apps locally +# Run frontend app locally cd apps/web && npm run dev # http://localhost:3001 -cd apps/platform && npm run dev # http://localhost:3000 # Or via Docker make up-web # Start web app container -make up-platform # Start platform app container # Build pnpm build # Build all packages/apps diff --git a/Makefile b/Makefile index 9a8923aa..5a66b07a 100644 --- a/Makefile +++ b/Makefile @@ -12,14 +12,11 @@ # make quickstart - Interactive setup and run (core: infra + API + MCP web search + web app) # make setup - Initial project setup (dependencies, networks, .env) # make cli-install - Install jan-cli tool globally -# make build-all - Build all Docker images (including platform & web) +# make build-all - Build all Docker images (including web app) # make up-full - Start services based on COMPOSE_PROFILES in .env -# make up-platform - Start platform web app (http://localhost:3000) # make up-web - Start web chat app (http://localhost:3001) # make dev-full - Start all services with host.docker.internal support (for testing) -# make swagger - Generate swagger docs and sync to platform -# make sync-docs - Sync /docs to apps/platform/content/docs -# make sync-swagger - Sync swagger files to apps/platform/api +# make swagger - Generate swagger docs # make health-check - Check if all services are healthy # make test-all - Run all integration tests # make stop - Stop all services (keeps containers & volumes) @@ -244,41 +241,16 @@ cli-clean: # --- Swagger Documentation --- -.PHONY: swagger swagger-llm-api swagger-media-api swagger-mcp-tools swagger-response-api swagger-realtime-api swagger-combine swagger-install sync-docs sync-swagger +.PHONY: swagger swagger-llm-api swagger-media-api swagger-mcp-tools swagger-response-api swagger-realtime-api swagger-combine swagger-install -swagger: cli-build sync-swagger +swagger: cli-build @echo "Generating Swagger documentation for all services..." ifeq ($(OS),Windows_NT) @powershell -ExecutionPolicy Bypass -File tools/jan-cli.ps1 swagger generate --combine - @echo "Syncing swagger to platform..." - @copy /Y "services\llm-api\docs\swagger\swagger-combined.json" "apps\platform\api\server.json" >nul 2>&1 || echo "swagger-combined.json not found" - @copy /Y "services\llm-api\docs\swagger\swagger.yaml" "apps\platform\api\server.yaml" >nul 2>&1 || echo "swagger.yaml not found" else @bash tools/jan-cli.sh swagger generate --combine - @echo "Syncing swagger to platform..." - @cp -f services/llm-api/docs/swagger/swagger-combined.json apps/platform/api/server.json 2>/dev/null || echo "swagger-combined.json not found" - @cp -f services/llm-api/docs/swagger/swagger.yaml apps/platform/api/server.yaml 2>/dev/null || echo "swagger.yaml not found" -endif - @echo " Swagger synced to apps/platform/api/" - -sync-swagger: - @echo "Syncing swagger files to platform..." -ifeq ($(OS),Windows_NT) - @if exist "services\llm-api\docs\swagger\swagger-combined.json" copy /Y "services\llm-api\docs\swagger\swagger-combined.json" "apps\platform\api\server.json" >nul - @if exist "services\llm-api\docs\swagger\swagger.yaml" copy /Y "services\llm-api\docs\swagger\swagger.yaml" "apps\platform\api\server.yaml" >nul -else - @cp -f services/llm-api/docs/swagger/swagger-combined.json apps/platform/api/server.json 2>/dev/null || true - @cp -f services/llm-api/docs/swagger/swagger.yaml apps/platform/api/server.yaml 2>/dev/null || true -endif - @echo " Swagger synced to apps/platform/api/" - -sync-docs: cli-build - @echo "Syncing docs to platform content..." -ifeq ($(OS),Windows_NT) - @cd tools/jan-cli && jan-cli.exe docs sync -else - @cd tools/jan-cli && ./jan-cli docs sync endif + @echo " Swagger documentation generated" swagger-llm-api: @echo "Generating Swagger for llm-api service..." diff --git a/README.md b/README.md index 926773b3..bd99106b 100644 --- a/README.md +++ b/README.md @@ -106,7 +106,6 @@ Governance and quality: jan-server/ |-- apps/ # Frontend applications | |-- web/ # Chat UI (React + Vite, port 3001) -| |-- platform/ # Admin panel & docs (Next.js, port 3000) |-- services/ # Go microservices | |-- llm-api/ | |-- response-api/ @@ -145,18 +144,15 @@ Key directories: | Application | Purpose | Port | Source | Tech Stack | | ----------- | -------------------------------------------- | ---- | ---------------- | ----------------------------------- | | Web App | Chat UI for conversations | 3001 | `apps/web` | React 19, Vite, TanStack Router | -| Platform | Admin panel & documentation | 3000 | `apps/platform` | Next.js 16, Fumadocs | -**Shared Package:** `packages/interfaces` - UI components (shadcn/ui), hooks, and utilities used by both apps. +**Shared Package:** `packages/interfaces` - UI components (shadcn/ui), hooks, and utilities used by the web app. ```bash -# Run frontend apps +# Run frontend app cd apps/web && npm install && npm run dev # http://localhost:3001 -cd apps/platform && npm install && npm run dev # http://localhost:3000 # Or via Docker make up-web # Start web app container -make up-platform # Start platform app container ``` ### Microservices Overview diff --git a/apps/platform/.dockerignore b/apps/platform/.dockerignore deleted file mode 100644 index b6cb09bd..00000000 --- a/apps/platform/.dockerignore +++ /dev/null @@ -1,16 +0,0 @@ -node_modules -.next -out -.git -.gitignore -*.md -.vscode -.idea -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -.DS_Store -*.pem -.env*.local diff --git a/apps/platform/.env.example b/apps/platform/.env.example deleted file mode 100644 index 8b5510c1..00000000 --- a/apps/platform/.env.example +++ /dev/null @@ -1 +0,0 @@ -NEXT_PUBLIC_JAN_BASE_URL=YOUR_NEXT_PUBLIC_JAN_BASE_URL \ No newline at end of file diff --git a/apps/platform/.gitignore b/apps/platform/.gitignore deleted file mode 100644 index 9633d40d..00000000 --- a/apps/platform/.gitignore +++ /dev/null @@ -1,44 +0,0 @@ -# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. - -# dependencies -/node_modules -/.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions - -# testing -/coverage - -# next.js -/.next/ -/out/ - -# production -/build - -# misc -.DS_Store -*.pem - -# debug -npm-debug.log* -yarn-debug.log* -yarn-error.log* -.pnpm-debug.log* - -# env files (can opt-in for committing if needed) -.env - -# vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts - -public/robots.txt -public/sitemap.xml \ No newline at end of file diff --git a/apps/platform/.prettierrc.json b/apps/platform/.prettierrc.json deleted file mode 100644 index 62ac1233..00000000 --- a/apps/platform/.prettierrc.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "printWidth": 100, - "semi": true, - "singleQuote": true, - "trailingComma": "all", - "arrowParens": "always", - "plugins": ["prettier-plugin-tailwindcss", "prettier-plugin-organize-imports"], - "overrides": [ - { - "files": ["*.mdx"], - "options": { - "proseWrap": "always" - } - } - ] -} diff --git a/apps/platform/.source/index.ts b/apps/platform/.source/index.ts deleted file mode 100644 index 1484c29a..00000000 --- a/apps/platform/.source/index.ts +++ /dev/null @@ -1,89 +0,0 @@ -// @ts-nocheck -- skip type checking -import * as docs_84 from "../content/docs/api-reference/admin-model-api/models/provider-models/bulk-toggle/post.mdx?collection=docs" -import * as docs_83 from "../content/docs/api-reference/admin-model-api/models/catalogs/bulk-toggle/post.mdx?collection=docs" -import * as docs_82 from "../content/docs/api-reference/model-api/models/providers/get.mdx?collection=docs" -import * as docs_81 from "../content/docs/api-reference/model-api/models/catalogs/get.mdx?collection=docs" -import * as docs_80 from "../content/docs/api-reference/conversations-api/conv-public-id/items/post.mdx?collection=docs" -import * as docs_79 from "../content/docs/api-reference/conversations-api/conv-public-id/items/get.mdx?collection=docs" -import * as docs_78 from "../content/docs/api-reference/conversations-api/conv-public-id/items/delete.mdx?collection=docs" -import * as docs_77 from "../content/docs/api-reference/admin-model-api/models/provider-models/patch.mdx?collection=docs" -import * as docs_76 from "../content/docs/api-reference/admin-model-api/models/provider-models/get.mdx?collection=docs" -import * as docs_75 from "../content/docs/api-reference/admin-model-api/models/catalogs/patch.mdx?collection=docs" -import * as docs_74 from "../content/docs/api-reference/admin-model-api/models/catalogs/get.mdx?collection=docs" -import * as docs_73 from "../content/docs/api-reference/admin-provider-api/providers/post.mdx?collection=docs" -import * as docs_72 from "../content/docs/api-reference/admin-provider-api/providers/patch.mdx?collection=docs" -import * as docs_71 from "../content/docs/api-reference/admin-provider-api/providers/get.mdx?collection=docs" -import * as docs_70 from "../content/docs/api-reference/server-api/version/get.mdx?collection=docs" -import * as docs_69 from "../content/docs/api-reference/server-api/readyz/get.mdx?collection=docs" -import * as docs_68 from "../content/docs/api-reference/server-api/healthz/get.mdx?collection=docs" -import * as docs_67 from "../content/docs/api-reference/projects-api/projects/post.mdx?collection=docs" -import * as docs_66 from "../content/docs/api-reference/projects-api/projects/patch.mdx?collection=docs" -import * as docs_65 from "../content/docs/api-reference/projects-api/projects/get.mdx?collection=docs" -import * as docs_64 from "../content/docs/api-reference/projects-api/projects/delete.mdx?collection=docs" -import * as docs_63 from "../content/docs/api-reference/conversations-api/conversations/post.mdx?collection=docs" -import * as docs_62 from "../content/docs/api-reference/conversations-api/conversations/get.mdx?collection=docs" -import * as docs_61 from "../content/docs/api-reference/conversations-api/conversations/delete.mdx?collection=docs" -import * as docs_60 from "../content/docs/api-reference/chat-completions-api/models/get.mdx?collection=docs" -import * as docs_59 from "../content/docs/api-reference/chat-completions-api/completions/post.mdx?collection=docs" -import * as docs_58 from "../content/docs/api-reference/authentication-api/validate-api-key/post.mdx?collection=docs" -import * as docs_57 from "../content/docs/api-reference/authentication-api/validate/post.mdx?collection=docs" -import * as docs_56 from "../content/docs/api-reference/authentication-api/revoke/post.mdx?collection=docs" -import * as docs_55 from "../content/docs/api-reference/authentication-api/refresh-token/post.mdx?collection=docs" -import * as docs_54 from "../content/docs/api-reference/authentication-api/upgrade/post.mdx?collection=docs" -import * as docs_53 from "../content/docs/api-reference/authentication-api/logout/get.mdx?collection=docs" -import * as docs_52 from "../content/docs/api-reference/authentication-api/me/get.mdx?collection=docs" -import * as docs_51 from "../content/docs/api-reference/authentication-api/guest-login/post.mdx?collection=docs" -import * as docs_50 from "../content/docs/api-reference/authentication-api/login/get.mdx?collection=docs" -import * as docs_49 from "../content/docs/api-reference/authentication-api/callback/get.mdx?collection=docs" -import * as docs_48 from "../content/docs/api-reference/authentication-api/api-keys/post.mdx?collection=docs" -import * as docs_47 from "../content/docs/api-reference/authentication-api/api-keys/get.mdx?collection=docs" -import * as docs_46 from "../content/docs/api-reference/authentication-api/api-keys/delete.mdx?collection=docs" -import * as docs_45 from "../content/docs/runbooks/monitoring.mdx?collection=docs" -import * as docs_44 from "../content/docs/runbooks/index.mdx?collection=docs" -import * as docs_43 from "../content/docs/architecture/test-flows.mdx?collection=docs" -import * as docs_42 from "../content/docs/architecture/system-design.mdx?collection=docs" -import * as docs_41 from "../content/docs/architecture/services.mdx?collection=docs" -import * as docs_40 from "../content/docs/architecture/security.mdx?collection=docs" -import * as docs_39 from "../content/docs/architecture/security-advanced.mdx?collection=docs" -import * as docs_38 from "../content/docs/architecture/observability.mdx?collection=docs" -import * as docs_37 from "../content/docs/architecture/index.mdx?collection=docs" -import * as docs_36 from "../content/docs/architecture/data-flow.mdx?collection=docs" -import * as docs_35 from "../content/docs/conventions/workflow.mdx?collection=docs" -import * as docs_34 from "../content/docs/conventions/design-patterns.mdx?collection=docs" -import * as docs_33 from "../content/docs/conventions/conventions.mdx?collection=docs" -import * as docs_32 from "../content/docs/conventions/architecture-patterns.mdx?collection=docs" -import * as docs_31 from "../content/docs/guides/webhooks.mdx?collection=docs" -import * as docs_30 from "../content/docs/guides/user-settings-personalization.mdx?collection=docs" -import * as docs_29 from "../content/docs/guides/user-management-todo.mdx?collection=docs" -import * as docs_28 from "../content/docs/guides/troubleshooting.mdx?collection=docs" -import * as docs_27 from "../content/docs/guides/testing.mdx?collection=docs" -import * as docs_26 from "../content/docs/guides/services-template.mdx?collection=docs" -import * as docs_25 from "../content/docs/guides/prompt-orchestration.mdx?collection=docs" -import * as docs_24 from "../content/docs/guides/monitoring.mdx?collection=docs" -import * as docs_23 from "../content/docs/guides/monitoring-advanced.mdx?collection=docs" -import * as docs_22 from "../content/docs/guides/mcp-testing.mdx?collection=docs" -import * as docs_21 from "../content/docs/guides/mcp-admin-interface.mdx?collection=docs" -import * as docs_20 from "../content/docs/guides/kong-plugins.mdx?collection=docs" -import * as docs_19 from "../content/docs/guides/jan-cli.mdx?collection=docs" -import * as docs_18 from "../content/docs/guides/index.mdx?collection=docs" -import * as docs_17 from "../content/docs/guides/development.mdx?collection=docs" -import * as docs_16 from "../content/docs/guides/deployment.mdx?collection=docs" -import * as docs_15 from "../content/docs/guides/conversation-management.mdx?collection=docs" -import * as docs_14 from "../content/docs/guides/background-mode.mdx?collection=docs" -import * as docs_13 from "../content/docs/guides/authentication.mdx?collection=docs" -import * as docs_12 from "../content/docs/configuration/service-migration.mdx?collection=docs" -import * as docs_11 from "../content/docs/configuration/precedence.mdx?collection=docs" -import * as docs_10 from "../content/docs/configuration/kubernetes.mdx?collection=docs" -import * as docs_9 from "../content/docs/configuration/index.mdx?collection=docs" -import * as docs_8 from "../content/docs/configuration/env-var-mapping.mdx?collection=docs" -import * as docs_7 from "../content/docs/configuration/docker-compose.mdx?collection=docs" -import * as docs_6 from "../content/docs/api-reference/introduction.mdx?collection=docs" -import * as docs_5 from "../content/docs/api-reference/debugging-requests.mdx?collection=docs" -import * as docs_4 from "../content/docs/api-reference/chat-completions-api.mdx?collection=docs" -import * as docs_3 from "../content/docs/api-reference/backward-compatibility.mdx?collection=docs" -import * as docs_2 from "../content/docs/roadmap.mdx?collection=docs" -import * as docs_1 from "../content/docs/repo-naming.mdx?collection=docs" -import * as docs_0 from "../content/docs/quickstart.mdx?collection=docs" -import { _runtime } from "fumadocs-mdx/runtime/next" -import * as _source from "../source.config" -export const docs = _runtime.docs([{ info: {"path":"quickstart.mdx","fullPath":"content\\docs\\quickstart.mdx"}, data: docs_0 }, { info: {"path":"repo-naming.mdx","fullPath":"content\\docs\\repo-naming.mdx"}, data: docs_1 }, { info: {"path":"roadmap.mdx","fullPath":"content\\docs\\roadmap.mdx"}, data: docs_2 }, { info: {"path":"api-reference/backward-compatibility.mdx","fullPath":"content\\docs\\api-reference\\backward-compatibility.mdx"}, data: docs_3 }, { info: {"path":"api-reference/chat-completions-api.mdx","fullPath":"content\\docs\\api-reference\\chat-completions-api.mdx"}, data: docs_4 }, { info: {"path":"api-reference/debugging-requests.mdx","fullPath":"content\\docs\\api-reference\\debugging-requests.mdx"}, data: docs_5 }, { info: {"path":"api-reference/introduction.mdx","fullPath":"content\\docs\\api-reference\\introduction.mdx"}, data: docs_6 }, { info: {"path":"configuration/docker-compose.mdx","fullPath":"content\\docs\\configuration\\docker-compose.mdx"}, data: docs_7 }, { info: {"path":"configuration/env-var-mapping.mdx","fullPath":"content\\docs\\configuration\\env-var-mapping.mdx"}, data: docs_8 }, { info: {"path":"configuration/index.mdx","fullPath":"content\\docs\\configuration\\index.mdx"}, data: docs_9 }, { info: {"path":"configuration/kubernetes.mdx","fullPath":"content\\docs\\configuration\\kubernetes.mdx"}, data: docs_10 }, { info: {"path":"configuration/precedence.mdx","fullPath":"content\\docs\\configuration\\precedence.mdx"}, data: docs_11 }, { info: {"path":"configuration/service-migration.mdx","fullPath":"content\\docs\\configuration\\service-migration.mdx"}, data: docs_12 }, { info: {"path":"guides/authentication.mdx","fullPath":"content\\docs\\guides\\authentication.mdx"}, data: docs_13 }, { info: {"path":"guides/background-mode.mdx","fullPath":"content\\docs\\guides\\background-mode.mdx"}, data: docs_14 }, { info: {"path":"guides/conversation-management.mdx","fullPath":"content\\docs\\guides\\conversation-management.mdx"}, data: docs_15 }, { info: {"path":"guides/deployment.mdx","fullPath":"content\\docs\\guides\\deployment.mdx"}, data: docs_16 }, { info: {"path":"guides/development.mdx","fullPath":"content\\docs\\guides\\development.mdx"}, data: docs_17 }, { info: {"path":"guides/index.mdx","fullPath":"content\\docs\\guides\\index.mdx"}, data: docs_18 }, { info: {"path":"guides/jan-cli.mdx","fullPath":"content\\docs\\guides\\jan-cli.mdx"}, data: docs_19 }, { info: {"path":"guides/kong-plugins.mdx","fullPath":"content\\docs\\guides\\kong-plugins.mdx"}, data: docs_20 }, { info: {"path":"guides/mcp-admin-interface.mdx","fullPath":"content\\docs\\guides\\mcp-admin-interface.mdx"}, data: docs_21 }, { info: {"path":"guides/mcp-testing.mdx","fullPath":"content\\docs\\guides\\mcp-testing.mdx"}, data: docs_22 }, { info: {"path":"guides/monitoring-advanced.mdx","fullPath":"content\\docs\\guides\\monitoring-advanced.mdx"}, data: docs_23 }, { info: {"path":"guides/monitoring.mdx","fullPath":"content\\docs\\guides\\monitoring.mdx"}, data: docs_24 }, { info: {"path":"guides/prompt-orchestration.mdx","fullPath":"content\\docs\\guides\\prompt-orchestration.mdx"}, data: docs_25 }, { info: {"path":"guides/services-template.mdx","fullPath":"content\\docs\\guides\\services-template.mdx"}, data: docs_26 }, { info: {"path":"guides/testing.mdx","fullPath":"content\\docs\\guides\\testing.mdx"}, data: docs_27 }, { info: {"path":"guides/troubleshooting.mdx","fullPath":"content\\docs\\guides\\troubleshooting.mdx"}, data: docs_28 }, { info: {"path":"guides/user-management-todo.mdx","fullPath":"content\\docs\\guides\\user-management-todo.mdx"}, data: docs_29 }, { info: {"path":"guides/user-settings-personalization.mdx","fullPath":"content\\docs\\guides\\user-settings-personalization.mdx"}, data: docs_30 }, { info: {"path":"guides/webhooks.mdx","fullPath":"content\\docs\\guides\\webhooks.mdx"}, data: docs_31 }, { info: {"path":"conventions/architecture-patterns.mdx","fullPath":"content\\docs\\conventions\\architecture-patterns.mdx"}, data: docs_32 }, { info: {"path":"conventions/conventions.mdx","fullPath":"content\\docs\\conventions\\conventions.mdx"}, data: docs_33 }, { info: {"path":"conventions/design-patterns.mdx","fullPath":"content\\docs\\conventions\\design-patterns.mdx"}, data: docs_34 }, { info: {"path":"conventions/workflow.mdx","fullPath":"content\\docs\\conventions\\workflow.mdx"}, data: docs_35 }, { info: {"path":"architecture/data-flow.mdx","fullPath":"content\\docs\\architecture\\data-flow.mdx"}, data: docs_36 }, { info: {"path":"architecture/index.mdx","fullPath":"content\\docs\\architecture\\index.mdx"}, data: docs_37 }, { info: {"path":"architecture/observability.mdx","fullPath":"content\\docs\\architecture\\observability.mdx"}, data: docs_38 }, { info: {"path":"architecture/security-advanced.mdx","fullPath":"content\\docs\\architecture\\security-advanced.mdx"}, data: docs_39 }, { info: {"path":"architecture/security.mdx","fullPath":"content\\docs\\architecture\\security.mdx"}, data: docs_40 }, { info: {"path":"architecture/services.mdx","fullPath":"content\\docs\\architecture\\services.mdx"}, data: docs_41 }, { info: {"path":"architecture/system-design.mdx","fullPath":"content\\docs\\architecture\\system-design.mdx"}, data: docs_42 }, { info: {"path":"architecture/test-flows.mdx","fullPath":"content\\docs\\architecture\\test-flows.mdx"}, data: docs_43 }, { info: {"path":"runbooks/index.mdx","fullPath":"content\\docs\\runbooks\\index.mdx"}, data: docs_44 }, { info: {"path":"runbooks/monitoring.mdx","fullPath":"content\\docs\\runbooks\\monitoring.mdx"}, data: docs_45 }, { info: {"path":"api-reference/authentication-api/api-keys/delete.mdx","fullPath":"content\\docs\\api-reference\\authentication-api\\api-keys\\delete.mdx"}, data: docs_46 }, { info: {"path":"api-reference/authentication-api/api-keys/get.mdx","fullPath":"content\\docs\\api-reference\\authentication-api\\api-keys\\get.mdx"}, data: docs_47 }, { info: {"path":"api-reference/authentication-api/api-keys/post.mdx","fullPath":"content\\docs\\api-reference\\authentication-api\\api-keys\\post.mdx"}, data: docs_48 }, { info: {"path":"api-reference/authentication-api/callback/get.mdx","fullPath":"content\\docs\\api-reference\\authentication-api\\callback\\get.mdx"}, data: docs_49 }, { info: {"path":"api-reference/authentication-api/login/get.mdx","fullPath":"content\\docs\\api-reference\\authentication-api\\login\\get.mdx"}, data: docs_50 }, { info: {"path":"api-reference/authentication-api/guest-login/post.mdx","fullPath":"content\\docs\\api-reference\\authentication-api\\guest-login\\post.mdx"}, data: docs_51 }, { info: {"path":"api-reference/authentication-api/me/get.mdx","fullPath":"content\\docs\\api-reference\\authentication-api\\me\\get.mdx"}, data: docs_52 }, { info: {"path":"api-reference/authentication-api/logout/get.mdx","fullPath":"content\\docs\\api-reference\\authentication-api\\logout\\get.mdx"}, data: docs_53 }, { info: {"path":"api-reference/authentication-api/upgrade/post.mdx","fullPath":"content\\docs\\api-reference\\authentication-api\\upgrade\\post.mdx"}, data: docs_54 }, { info: {"path":"api-reference/authentication-api/refresh-token/post.mdx","fullPath":"content\\docs\\api-reference\\authentication-api\\refresh-token\\post.mdx"}, data: docs_55 }, { info: {"path":"api-reference/authentication-api/revoke/post.mdx","fullPath":"content\\docs\\api-reference\\authentication-api\\revoke\\post.mdx"}, data: docs_56 }, { info: {"path":"api-reference/authentication-api/validate/post.mdx","fullPath":"content\\docs\\api-reference\\authentication-api\\validate\\post.mdx"}, data: docs_57 }, { info: {"path":"api-reference/authentication-api/validate-api-key/post.mdx","fullPath":"content\\docs\\api-reference\\authentication-api\\validate-api-key\\post.mdx"}, data: docs_58 }, { info: {"path":"api-reference/chat-completions-api/completions/post.mdx","fullPath":"content\\docs\\api-reference\\chat-completions-api\\completions\\post.mdx"}, data: docs_59 }, { info: {"path":"api-reference/chat-completions-api/models/get.mdx","fullPath":"content\\docs\\api-reference\\chat-completions-api\\models\\get.mdx"}, data: docs_60 }, { info: {"path":"api-reference/conversations-api/conversations/delete.mdx","fullPath":"content\\docs\\api-reference\\conversations-api\\conversations\\delete.mdx"}, data: docs_61 }, { info: {"path":"api-reference/conversations-api/conversations/get.mdx","fullPath":"content\\docs\\api-reference\\conversations-api\\conversations\\get.mdx"}, data: docs_62 }, { info: {"path":"api-reference/conversations-api/conversations/post.mdx","fullPath":"content\\docs\\api-reference\\conversations-api\\conversations\\post.mdx"}, data: docs_63 }, { info: {"path":"api-reference/projects-api/projects/delete.mdx","fullPath":"content\\docs\\api-reference\\projects-api\\projects\\delete.mdx"}, data: docs_64 }, { info: {"path":"api-reference/projects-api/projects/get.mdx","fullPath":"content\\docs\\api-reference\\projects-api\\projects\\get.mdx"}, data: docs_65 }, { info: {"path":"api-reference/projects-api/projects/patch.mdx","fullPath":"content\\docs\\api-reference\\projects-api\\projects\\patch.mdx"}, data: docs_66 }, { info: {"path":"api-reference/projects-api/projects/post.mdx","fullPath":"content\\docs\\api-reference\\projects-api\\projects\\post.mdx"}, data: docs_67 }, { info: {"path":"api-reference/server-api/healthz/get.mdx","fullPath":"content\\docs\\api-reference\\server-api\\healthz\\get.mdx"}, data: docs_68 }, { info: {"path":"api-reference/server-api/readyz/get.mdx","fullPath":"content\\docs\\api-reference\\server-api\\readyz\\get.mdx"}, data: docs_69 }, { info: {"path":"api-reference/server-api/version/get.mdx","fullPath":"content\\docs\\api-reference\\server-api\\version\\get.mdx"}, data: docs_70 }, { info: {"path":"api-reference/admin-provider-api/providers/get.mdx","fullPath":"content\\docs\\api-reference\\admin-provider-api\\providers\\get.mdx"}, data: docs_71 }, { info: {"path":"api-reference/admin-provider-api/providers/patch.mdx","fullPath":"content\\docs\\api-reference\\admin-provider-api\\providers\\patch.mdx"}, data: docs_72 }, { info: {"path":"api-reference/admin-provider-api/providers/post.mdx","fullPath":"content\\docs\\api-reference\\admin-provider-api\\providers\\post.mdx"}, data: docs_73 }, { info: {"path":"api-reference/admin-model-api/models/catalogs/get.mdx","fullPath":"content\\docs\\api-reference\\admin-model-api\\models\\catalogs\\get.mdx"}, data: docs_74 }, { info: {"path":"api-reference/admin-model-api/models/catalogs/patch.mdx","fullPath":"content\\docs\\api-reference\\admin-model-api\\models\\catalogs\\patch.mdx"}, data: docs_75 }, { info: {"path":"api-reference/admin-model-api/models/provider-models/get.mdx","fullPath":"content\\docs\\api-reference\\admin-model-api\\models\\provider-models\\get.mdx"}, data: docs_76 }, { info: {"path":"api-reference/admin-model-api/models/provider-models/patch.mdx","fullPath":"content\\docs\\api-reference\\admin-model-api\\models\\provider-models\\patch.mdx"}, data: docs_77 }, { info: {"path":"api-reference/conversations-api/conv-public-id/items/delete.mdx","fullPath":"content\\docs\\api-reference\\conversations-api\\conv-public-id\\items\\delete.mdx"}, data: docs_78 }, { info: {"path":"api-reference/conversations-api/conv-public-id/items/get.mdx","fullPath":"content\\docs\\api-reference\\conversations-api\\conv-public-id\\items\\get.mdx"}, data: docs_79 }, { info: {"path":"api-reference/conversations-api/conv-public-id/items/post.mdx","fullPath":"content\\docs\\api-reference\\conversations-api\\conv-public-id\\items\\post.mdx"}, data: docs_80 }, { info: {"path":"api-reference/model-api/models/catalogs/get.mdx","fullPath":"content\\docs\\api-reference\\model-api\\models\\catalogs\\get.mdx"}, data: docs_81 }, { info: {"path":"api-reference/model-api/models/providers/get.mdx","fullPath":"content\\docs\\api-reference\\model-api\\models\\providers\\get.mdx"}, data: docs_82 }, { info: {"path":"api-reference/admin-model-api/models/catalogs/bulk-toggle/post.mdx","fullPath":"content\\docs\\api-reference\\admin-model-api\\models\\catalogs\\bulk-toggle\\post.mdx"}, data: docs_83 }, { info: {"path":"api-reference/admin-model-api/models/provider-models/bulk-toggle/post.mdx","fullPath":"content\\docs\\api-reference\\admin-model-api\\models\\provider-models\\bulk-toggle\\post.mdx"}, data: docs_84 }], [{"info":{"path":"meta.json","fullPath":"content\\docs\\meta.json"},"data":{"title":"Documentation","pages":["quickstart","guides","configuration","architecture","conventions","runbooks","api-reference"],"description":"Jan Platform Documentation","root":true}}, {"info":{"path":"architecture/meta.json","fullPath":"content\\docs\\architecture\\meta.json"},"data":{"title":"Architecture","pages":["index","system-design","services","data-flow","security","security-advanced","observability","test-flows"]}}, {"info":{"path":"configuration/meta.json","fullPath":"content\\docs\\configuration\\meta.json"},"data":{"title":"Configuration","pages":["index","docker-compose","kubernetes","env-var-mapping","precedence","service-migration"]}}, {"info":{"path":"conventions/meta.json","fullPath":"content\\docs\\conventions\\meta.json"},"data":{"title":"Conventions","pages":["index","conventions","architecture-patterns","design-patterns","workflow"]}}, {"info":{"path":"runbooks/meta.json","fullPath":"content\\docs\\runbooks\\meta.json"},"data":{"title":"Runbooks","pages":["index","monitoring"]}}, {"info":{"path":"api-reference/meta.json","fullPath":"content\\docs\\api-reference\\meta.json"},"data":{"title":"API Reference","pages":["introduction","authentication-api","chat-completions-api","conversations-api","projects-api","model-api","admin-model-api","admin-provider-api","server-api","debugging-requests","backward-compatibility"],"description":"Jan Server API Reference","root":false}}, {"info":{"path":"api-reference/admin-provider-api/meta.json","fullPath":"content\\docs\\api-reference\\admin-provider-api\\meta.json"},"data":{"title":"Admin Provider API","pages":["providers/get","providers/post","providers/patch"]}}, {"info":{"path":"api-reference/admin-model-api/meta.json","fullPath":"content\\docs\\api-reference\\admin-model-api\\meta.json"},"data":{"title":"Admin Model API","pages":["models/catalogs/get","models/provider-models/get","models/catalogs/bulk-toggle/post","models/provider-models/bulk-toggle/post","models/catalogs/patch","models/provider-models/patch"]}}, {"info":{"path":"api-reference/chat-completions-api/meta.json","fullPath":"content\\docs\\api-reference\\chat-completions-api\\meta.json"},"data":{"title":"Chat Completions API","pages":["models/get","completions/post"]}}, {"info":{"path":"api-reference/conversations-api/meta.json","fullPath":"content\\docs\\api-reference\\conversations-api\\meta.json"},"data":{"title":"Conversations API","pages":["conv-public-id/items/get","conversations/get","conv-public-id/items/post","conversations/post","conv-public-id/items/delete","conversations/delete"]}}, {"info":{"path":"api-reference/model-api/meta.json","fullPath":"content\\docs\\api-reference\\model-api\\meta.json"},"data":{"title":"Model API","pages":["models/catalogs/get","models/providers/get"]}}, {"info":{"path":"api-reference/projects-api/meta.json","fullPath":"content\\docs\\api-reference\\projects-api\\meta.json"},"data":{"title":"Projects API","pages":["projects/get","projects/post","projects/patch","projects/delete"]}}, {"info":{"path":"api-reference/server-api/meta.json","fullPath":"content\\docs\\api-reference\\server-api\\meta.json"},"data":{"title":"Server API","pages":["healthz/get","readyz/get","version/get"]}}, {"info":{"path":"api-reference/authentication-api/meta.json","fullPath":"content\\docs\\api-reference\\authentication-api\\meta.json"},"data":{"title":"Authentication API","pages":["api-keys/get","callback/get","login/get","logout/get","me/get","api-keys/post","guest-login/post","refresh-token/post","revoke/post","upgrade/post","validate-api-key/post","validate/post","api-keys/delete"]}}, {"info":{"path":"guides/meta.json","fullPath":"content\\docs\\guides\\meta.json"},"data":{"title":"Guides","pages":["index","development","deployment","authentication","monitoring","monitoring-advanced","testing","troubleshooting","jan-cli","kong-plugins","mcp-testing","mcp-admin-interface","conversation-management","prompt-orchestration","background-mode","webhooks","services-template","user-settings-personalization","user-management-todo"]}}]) \ No newline at end of file diff --git a/apps/platform/.source/source.config.mjs b/apps/platform/.source/source.config.mjs deleted file mode 100644 index 38ee8662..00000000 --- a/apps/platform/.source/source.config.mjs +++ /dev/null @@ -1,30 +0,0 @@ -// source.config.ts -import { defineConfig, defineDocs, frontmatterSchema, metaSchema } from "fumadocs-mdx/config"; -import { rehypeCodeDefaultOptions } from "fumadocs-core/mdx-plugins"; -import { transformerTwoslash } from "fumadocs-twoslash"; -var docs = defineDocs({ - docs: { - schema: frontmatterSchema, - postprocess: { - includeProcessedMarkdown: true - } - }, - meta: { - schema: metaSchema - } -}); -var source_config_default = defineConfig({ - mdxOptions: { - rehypeCodeOptions: { - themes: { - light: "github-light", - dark: "github-dark" - }, - transformers: [...rehypeCodeDefaultOptions.transformers ?? [], transformerTwoslash()] - } - } -}); -export { - source_config_default as default, - docs -}; diff --git a/apps/platform/.yarnrc.yml b/apps/platform/.yarnrc.yml deleted file mode 100644 index 3186f3f0..00000000 --- a/apps/platform/.yarnrc.yml +++ /dev/null @@ -1 +0,0 @@ -nodeLinker: node-modules diff --git a/apps/platform/Dockerfile b/apps/platform/Dockerfile deleted file mode 100644 index 2d850e72..00000000 --- a/apps/platform/Dockerfile +++ /dev/null @@ -1,61 +0,0 @@ -# Base image with pnpm -FROM node:20-slim AS base -ENV PNPM_HOME="/pnpm" -ENV PATH="$PNPM_HOME:$PATH" -RUN corepack enable - -# Build stage -FROM base AS builder -WORKDIR /app - -# Copy workspace files -COPY pnpm-workspace.yaml package.json ./ -COPY .npmrc* ./ - -# Copy all workspace packages (needed for build dependencies) -COPY apps/platform ./apps/platform - -# Install all dependencies -RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install - -# Build arguments -ARG NEXT_PUBLIC_JAN_BASE_URL -ENV NEXT_PUBLIC_JAN_BASE_URL=$NEXT_PUBLIC_JAN_BASE_URL - -# Build the platform app -RUN pnpm build:platform - -# Production stage -FROM node:20-alpine AS runner - -WORKDIR /app - -ENV NODE_ENV=production -ENV PORT=3000 -ENV HOSTNAME="0.0.0.0" - -# Create non-root user -RUN addgroup --system --gid 1001 nodejs -RUN adduser --system --uid 1001 nextjs - -# Copy built application and dependencies from builder with ownership -COPY --from=builder --chown=nextjs:nodejs /app/apps/platform/.next ./apps/platform/.next -COPY --from=builder --chown=nextjs:nodejs /app/apps/platform/node_modules ./apps/platform/node_modules -COPY --from=builder --chown=nextjs:nodejs /app/apps/platform/package.json ./apps/platform/package.json -COPY --from=builder --chown=nextjs:nodejs /app/apps/platform/next.config.mjs ./apps/platform/next.config.mjs -COPY --from=builder --chown=nextjs:nodejs /app/apps/platform/source.config.ts ./apps/platform/source.config.ts -COPY --from=builder --chown=nextjs:nodejs /app/apps/platform/tsconfig.json ./apps/platform/tsconfig.json -COPY --from=builder --chown=nextjs:nodejs /app/apps/platform/content ./apps/platform/content -COPY --from=builder --chown=nextjs:nodejs /app/apps/platform/public ./apps/platform/public -COPY --from=builder --chown=nextjs:nodejs /app/apps/platform/src ./apps/platform/src - -# Copy root node_modules for monorepo dependencies -COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules - -USER nextjs - -WORKDIR /app/apps/platform - -EXPOSE 3000 - -CMD ["node_modules/.bin/next", "start"] \ No newline at end of file diff --git a/apps/platform/api/server.json b/apps/platform/api/server.json deleted file mode 100644 index 85db1681..00000000 --- a/apps/platform/api/server.json +++ /dev/null @@ -1,10089 +0,0 @@ -{ - "basePath": "/", - "definitions": { - "MCP_responses.ErrorResponse": { - "properties": { - "code": { - "description": "UUID from PlatformError", - "type": "string" - }, - "error": { - "type": "string" - }, - "request_id": { - "type": "string" - } - }, - "type": "object" - }, - "Realtime_responses.ErrorDetail": { - "properties": { - "code": { - "type": "string" - }, - "message": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "type": "object" - }, - "Realtime_responses.ErrorResponse": { - "properties": { - "error": { - "$ref": "#/definitions/Realtime_responses.ErrorDetail" - } - }, - "type": "object" - }, - "Realtime_sessionres.ClientSecretDetail": { - "properties": { - "expires_at": { - "type": "integer" - }, - "value": { - "type": "string" - } - }, - "type": "object" - }, - "Realtime_sessionres.DeleteSessionResponse": { - "properties": { - "deleted": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "object": { - "type": "string" - } - }, - "type": "object" - }, - "Realtime_sessionres.ListSessionsResponse": { - "properties": { - "data": { - "items": { - "$ref": "#/definitions/Realtime_sessionres.SessionResponse" - }, - "type": "array" - }, - "object": { - "type": "string" - } - }, - "type": "object" - }, - "Realtime_sessionres.SessionResponse": { - "properties": { - "client_secret": { - "$ref": "#/definitions/Realtime_sessionres.ClientSecretDetail" - }, - "id": { - "type": "string" - }, - "object": { - "type": "string" - }, - "room_id": { - "type": "string" - }, - "status": { - "type": "string" - }, - "user_id": { - "type": "string" - }, - "ws_url": { - "type": "string" - } - }, - "type": "object" - }, - "authhandler.AccessTokenResponse": { - "properties": { - "access_token": { - "type": "string" - }, - "expires_at": { - "type": "string" - }, - "expires_in": { - "type": "integer" - }, - "refresh_token": { - "type": "string" - }, - "token_type": { - "type": "string" - } - }, - "type": "object" - }, - "authhandler.GetMeResponse": { - "properties": { - "auth_method": { - "type": "string" - }, - "email": { - "type": "string" - }, - "id": { - "type": "string" - }, - "is_admin": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "roles": { - "items": { - "type": "string" - }, - "type": "array" - }, - "subject": { - "type": "string" - }, - "username": { - "type": "string" - } - }, - "type": "object" - }, - "chatrequests.ChatCompletionRequest": { - "properties": { - "chat_template_kwargs": { - "additionalProperties": {}, - "description": "ChatTemplateKwargs provides a way to add non-standard parameters to the request body.\nAdditional kwargs to pass to the template renderer. Will be accessible by the chat template.\nSuch as think mode for qwen3. \"chat_template_kwargs\": {\"enable_thinking\": false}\nhttps://qwen.readthedocs.io/en/latest/deployment/vllm.html#thinking-non-thinking-modes", - "type": "object" - }, - "conversation": { - "allOf": [ - { - "$ref": "#/definitions/chatrequests.ConversationReference" - } - ], - "description": "Conversation can be either a string (conversation ID) or a conversation object\nItems from this conversation are prepended to Messages for this response request.\nInput items and output items from this response are automatically added to this conversation after completion." - }, - "deep_research": { - "description": "DeepResearch enables the Deep Research mode which uses a specialized prompt\nfor conducting in-depth investigations with tool usage.\nRequires a model with supports_reasoning: true capability.", - "type": "boolean" - }, - "enable_thinking": { - "description": "EnableThinking controls whether reasoning/thinking capabilities should be used.\nDefaults to true. When set to false for a model with supports_reasoning: true\nand an instruct model configured, the instruct model will be used instead.", - "type": "boolean" - }, - "frequency_penalty": { - "type": "number" - }, - "function_call": { - "description": "Deprecated: use ToolChoice instead." - }, - "functions": { - "description": "Deprecated: use Tools instead.", - "items": { - "$ref": "#/definitions/openai.FunctionDefinition" - }, - "type": "array" - }, - "guided_choice": { - "description": "GuidedChoice is a vLLM-specific extension that restricts the model's output\nto one of the predefined string choices provided in this field. This feature\nis used to constrain the model's responses to a controlled set of options,\nensuring predictable and consistent outputs in scenarios where specific\nchoices are required.", - "items": { - "type": "string" - }, - "type": "array" - }, - "logit_bias": { - "additionalProperties": { - "type": "integer" - }, - "description": "LogitBias is must be a token id string (specified by their token ID in the tokenizer), not a word string.\nincorrect: `\"logit_bias\":{\"You\": 6}`, correct: `\"logit_bias\":{\"1639\": 6}`\nrefs: https://platform.openai.com/docs/api-reference/chat/create#chat/create-logit_bias", - "type": "object" - }, - "logprobs": { - "description": "LogProbs indicates whether to return log probabilities of the output tokens or not.\nIf true, returns the log probabilities of each output token returned in the content of message.\nThis option is currently not available on the gpt-4-vision-preview model.", - "type": "boolean" - }, - "max_completion_tokens": { - "description": "MaxCompletionTokens An upper bound for the number of tokens that can be generated for a completion,\nincluding visible output tokens and reasoning tokens https://platform.openai.com/docs/guides/reasoning", - "type": "integer" - }, - "max_tokens": { - "description": "MaxTokens The maximum number of tokens that can be generated in the chat completion.\nThis value can be used to control costs for text generated via API.\nDeprecated: use MaxCompletionTokens. Not compatible with o1-series models.\nrefs: https://platform.openai.com/docs/api-reference/chat/create#chat-create-max_tokens", - "type": "integer" - }, - "messages": { - "items": { - "$ref": "#/definitions/openai.ChatCompletionMessage" - }, - "type": "array" - }, - "metadata": { - "additionalProperties": { - "type": "string" - }, - "description": "Metadata to store with the completion.", - "type": "object" - }, - "model": { - "type": "string" - }, - "n": { - "type": "integer" - }, - "parallel_tool_calls": { - "description": "Disable the default behavior of parallel tool calls by setting it: false." - }, - "prediction": { - "allOf": [ - { - "$ref": "#/definitions/openai.Prediction" - } - ], - "description": "Configuration for a predicted output." - }, - "presence_penalty": { - "type": "number" - }, - "reasoning_effort": { - "description": "Controls effort on reasoning for reasoning models. It can be set to \"low\", \"medium\", or \"high\".", - "type": "string" - }, - "repetition_penalty": { - "type": "number" - }, - "response_format": { - "$ref": "#/definitions/openai.ChatCompletionResponseFormat" - }, - "safety_identifier": { - "description": "A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies.\nThe IDs should be a string that uniquely identifies each user.\nWe recommend hashing their username or email address, in order to avoid sending us any identifying information.\nhttps://platform.openai.com/docs/api-reference/chat/create#chat_create-safety_identifier", - "type": "string" - }, - "seed": { - "type": "integer" - }, - "service_tier": { - "allOf": [ - { - "$ref": "#/definitions/openai.ServiceTier" - } - ], - "description": "Specifies the latency tier to use for processing the request." - }, - "stop": { - "items": { - "type": "string" - }, - "type": "array" - }, - "store": { - "description": "Store controls whether the latest input and generated response should be persisted", - "type": "boolean" - }, - "store_reasoning": { - "description": "StoreReasoning controls whether reasoning content (if present) should also be persisted", - "type": "boolean" - }, - "stream": { - "type": "boolean" - }, - "stream_options": { - "allOf": [ - { - "$ref": "#/definitions/openai.StreamOptions" - } - ], - "description": "Options for streaming response. Only set this when you set stream: true." - }, - "temperature": { - "type": "number" - }, - "tool_choice": { - "description": "This can be either a string or an ToolChoice object." - }, - "tools": { - "items": { - "$ref": "#/definitions/openai.Tool" - }, - "type": "array" - }, - "top_k": { - "type": "integer" - }, - "top_logprobs": { - "description": "TopLogProbs is an integer between 0 and 5 specifying the number of most likely tokens to return at each\ntoken position, each with an associated log probability.\nlogprobs must be set to true if this parameter is used.", - "type": "integer" - }, - "top_p": { - "type": "number" - }, - "user": { - "type": "string" - }, - "verbosity": { - "description": "Verbosity determines how many output tokens are generated. Lowering the number of\ntokens reduces overall latency. It can be set to \"low\", \"medium\", or \"high\".\nNote: This field is only confirmed to work with gpt-5, gpt-5-mini and gpt-5-nano.\nAlso, it is not in the API reference of chat completion at the time of writing,\nthough it is supported by the API.", - "type": "string" - } - }, - "type": "object" - }, - "chatrequests.ConversationReference": { - "type": "object" - }, - "chatresponses.ChatCompletionResponse": { - "properties": { - "choices": { - "items": { - "$ref": "#/definitions/openai.ChatCompletionChoice" - }, - "type": "array" - }, - "conversation": { - "$ref": "#/definitions/chatresponses.ConversationContext" - }, - "created": { - "type": "integer" - }, - "id": { - "type": "string" - }, - "model": { - "type": "string" - }, - "object": { - "type": "string" - }, - "prompt_filter_results": { - "items": { - "$ref": "#/definitions/openai.PromptFilterResult" - }, - "type": "array" - }, - "service_tier": { - "$ref": "#/definitions/openai.ServiceTier" - }, - "system_fingerprint": { - "type": "string" - }, - "trimmed": { - "description": "True if messages were trimmed to fit context", - "type": "boolean" - }, - "usage": { - "$ref": "#/definitions/openai.Usage" - } - }, - "type": "object" - }, - "chatresponses.ConversationContext": { - "properties": { - "id": { - "description": "The unique ID of the conversation", - "type": "string" - }, - "title": { - "description": "The title of the conversation (optional)", - "type": "string" - } - }, - "type": "object" - }, - "conversation.Annotation": { - "properties": { - "bounding_box": { - "allOf": [ - { - "$ref": "#/definitions/conversation.BBox" - } - ], - "description": "Bounding box for image/PDF annotations" - }, - "confidence": { - "description": "Citation confidence score (0.0-1.0)", - "type": "number" - }, - "container_id": { - "description": "Document container reference", - "type": "string" - }, - "end_index": { - "description": "End position in text", - "type": "integer" - }, - "file_id": { - "description": "For file citations", - "type": "string" - }, - "filename": { - "description": "File name for citations", - "type": "string" - }, - "index": { - "description": "Citation index", - "type": "integer" - }, - "page_number": { - "description": "Page reference for documents", - "type": "integer" - }, - "quote": { - "description": "Actual quoted text from source", - "type": "string" - }, - "start_index": { - "description": "Start position in text", - "type": "integer" - }, - "text": { - "description": "Display text", - "type": "string" - }, - "type": { - "description": "\"file_citation\", \"url_citation\", \"file_path\", etc.", - "type": "string" - }, - "url": { - "description": "For URL citations", - "type": "string" - } - }, - "type": "object" - }, - "conversation.AudioContent": { - "properties": { - "data": { - "description": "Base64 encoded audio data", - "type": "string" - }, - "format": { - "description": "Audio format: mp3, wav, pcm16, etc.", - "type": "string" - }, - "id": { - "type": "string" - }, - "transcript": { - "description": "Text transcription of audio", - "type": "string" - } - }, - "type": "object" - }, - "conversation.BBox": { - "properties": { - "height": { - "type": "number" - }, - "width": { - "type": "number" - }, - "x": { - "type": "number" - }, - "y": { - "type": "number" - } - }, - "type": "object" - }, - "conversation.CodeContent": { - "properties": { - "code": { - "description": "Code content", - "type": "string" - }, - "error": { - "description": "Execution error", - "type": "string" - }, - "execution_id": { - "description": "Execution session ID", - "type": "string" - }, - "exit_code": { - "description": "Process exit code", - "type": "integer" - }, - "language": { - "description": "Programming language", - "type": "string" - }, - "metadata": { - "additionalProperties": {}, - "description": "Additional metadata", - "type": "object" - }, - "output": { - "description": "Execution output", - "type": "string" - } - }, - "type": "object" - }, - "conversation.ComputerAction": { - "properties": { - "action": { - "description": "Action type: \"click\", \"type\", \"key\", \"scroll\", \"move\", etc.", - "type": "string" - }, - "coordinates": { - "allOf": [ - { - "$ref": "#/definitions/conversation.Coordinates" - } - ], - "description": "Screen coordinates for mouse actions" - }, - "key": { - "description": "Key for keyboard actions", - "type": "string" - }, - "metadata": { - "additionalProperties": {}, - "description": "Additional action metadata", - "type": "object" - }, - "scroll_delta": { - "allOf": [ - { - "$ref": "#/definitions/conversation.ScrollDelta" - } - ], - "description": "Scroll amount" - }, - "text": { - "description": "Text for typing actions", - "type": "string" - } - }, - "type": "object" - }, - "conversation.Content": { - "properties": { - "audio": { - "allOf": [ - { - "$ref": "#/definitions/conversation.AudioContent" - } - ], - "description": "Audio content for speech" - }, - "code": { - "allOf": [ - { - "$ref": "#/definitions/conversation.CodeContent" - } - ], - "description": "Code block with execution metadata" - }, - "computer_action": { - "allOf": [ - { - "$ref": "#/definitions/conversation.ComputerAction" - } - ], - "description": "Computer interaction details" - }, - "computer_screenshot": { - "allOf": [ - { - "$ref": "#/definitions/conversation.ScreenshotContent" - } - ], - "description": "Screenshot from computer use" - }, - "file": { - "allOf": [ - { - "$ref": "#/definitions/conversation.FileContent" - } - ], - "description": "File content" - }, - "finish_reason": { - "description": "Finish reason", - "type": "string" - }, - "function_call": { - "allOf": [ - { - "$ref": "#/definitions/conversation.FunctionCall" - } - ], - "description": "Function call content (deprecated, use tool_calls)" - }, - "function_call_output": { - "allOf": [ - { - "$ref": "#/definitions/conversation.FunctionCallOut" - } - ], - "description": "Function call output" - }, - "image": { - "allOf": [ - { - "$ref": "#/definitions/conversation.ImageContent" - } - ], - "description": "Image content" - }, - "input_audio": { - "allOf": [ - { - "$ref": "#/definitions/conversation.InputAudio" - } - ], - "description": "User audio input" - }, - "output_text": { - "allOf": [ - { - "$ref": "#/definitions/conversation.OutputText" - } - ], - "description": "AI output text (with annotations)" - }, - "refusal": { - "description": "Model refusal message", - "type": "string" - }, - "summary_text": { - "description": "Summary content", - "type": "string" - }, - "thinking": { - "description": "Internal reasoning (o1 models)", - "type": "string" - }, - "tool_call_id": { - "description": "Tool call ID (for tool responses)", - "type": "string" - }, - "tool_calls": { - "description": "Tool calls (for assistant messages)", - "items": { - "$ref": "#/definitions/conversation.ToolCall" - }, - "type": "array" - }, - "type": { - "type": "string" - } - }, - "type": "object" - }, - "conversation.Coordinates": { - "properties": { - "x": { - "type": "integer" - }, - "y": { - "type": "integer" - } - }, - "type": "object" - }, - "conversation.FileContent": { - "properties": { - "file_id": { - "type": "string" - }, - "mime_type": { - "type": "string" - }, - "name": { - "type": "string" - }, - "size": { - "type": "integer" - } - }, - "type": "object" - }, - "conversation.FunctionCall": { - "properties": { - "arguments": { - "description": "JSON-encoded arguments", - "type": "string" - }, - "id": { - "description": "Call ID", - "type": "string" - }, - "name": { - "description": "Function name", - "type": "string" - } - }, - "type": "object" - }, - "conversation.FunctionCallOut": { - "properties": { - "call_id": { - "description": "ID of the function call this responds to", - "type": "string" - }, - "output": { - "description": "String output from the function", - "type": "string" - } - }, - "type": "object" - }, - "conversation.ImageContent": { - "properties": { - "detail": { - "description": "\"low\", \"high\", \"auto\"", - "type": "string" - }, - "file_id": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "type": "object" - }, - "conversation.IncompleteDetails": { - "properties": { - "error": { - "description": "Error message if applicable", - "type": "string" - }, - "reason": { - "description": "\"max_tokens\", \"content_filter\", \"tool_calls\", etc.", - "type": "string" - } - }, - "type": "object" - }, - "conversation.InputAudio": { - "properties": { - "data": { - "description": "Base64 encoded audio data", - "type": "string" - }, - "format": { - "description": "Audio format: mp3, wav, pcm16, etc.", - "type": "string" - }, - "transcript": { - "description": "Optional text transcription", - "type": "string" - } - }, - "type": "object" - }, - "conversation.Item": { - "properties": { - "acknowledged_safety_checks": { - "description": "For computer call outputs", - "items": { - "$ref": "#/definitions/conversation.SafetyCheck" - }, - "type": "array" - }, - "action": { - "additionalProperties": true, - "description": "For computer/shell actions", - "type": "object" - }, - "approval_request_id": { - "description": "For MCP approval", - "type": "string" - }, - "approve": { - "description": "For MCP approval response", - "type": "boolean" - }, - "arguments": { - "description": "For tool calls (JSON string)", - "type": "string" - }, - "branch": { - "description": "Branch identifier (MAIN, EDIT_1, etc.)", - "type": "string" - }, - "call_id": { - "description": "OpenAI-compatible fields for specific item types", - "type": "string" - }, - "commands": { - "description": "For shell calls", - "items": { - "type": "string" - }, - "type": "array" - }, - "completed_at": { - "type": "string" - }, - "content": { - "items": { - "$ref": "#/definitions/conversation.Content" - }, - "type": "array" - }, - "created_at": { - "type": "string" - }, - "error": { - "description": "For failed calls", - "type": "string" - }, - "id": { - "type": "string" - }, - "incomplete_at": { - "type": "string" - }, - "incomplete_details": { - "$ref": "#/definitions/conversation.IncompleteDetails" - }, - "max_output_length": { - "description": "For shell calls", - "type": "integer" - }, - "name": { - "description": "For MCP tool calls - tool name", - "type": "string" - }, - "object": { - "description": "Always \"conversation.item\" for OpenAI compatibility", - "type": "string" - }, - "operation": { - "additionalProperties": true, - "description": "For patch operations", - "type": "object" - }, - "output": { - "description": "For tool call outputs", - "type": "string" - }, - "pending_safety_checks": { - "description": "For computer calls", - "items": { - "$ref": "#/definitions/conversation.SafetyCheck" - }, - "type": "array" - }, - "rated_at": { - "description": "When rating was given", - "type": "string" - }, - "rating": { - "allOf": [ - { - "$ref": "#/definitions/conversation.ItemRating" - } - ], - "description": "User feedback/rating" - }, - "rating_comment": { - "description": "Optional comment with rating", - "type": "string" - }, - "reason": { - "description": "For MCP approval reason", - "type": "string" - }, - "role": { - "$ref": "#/definitions/conversation.ItemRole" - }, - "sequence_number": { - "description": "Order within branch", - "type": "integer" - }, - "server_label": { - "description": "For MCP calls", - "type": "string" - }, - "shell_outputs": { - "description": "For shell call outputs", - "items": { - "$ref": "#/definitions/conversation.ShellOutput" - }, - "type": "array" - }, - "status": { - "$ref": "#/definitions/conversation.ItemStatus" - }, - "tools": { - "description": "For mcp_list_tools", - "items": { - "$ref": "#/definitions/conversation.McpTool" - }, - "type": "array" - }, - "type": { - "$ref": "#/definitions/conversation.ItemType" - } - }, - "type": "object" - }, - "conversation.ItemRating": { - "enum": [ - "like", - "unlike" - ], - "type": "string", - "x-enum-comments": { - "ItemRatingLike": "Positive feedback (like)", - "ItemRatingUnlike": "Negative feedback (unlike)" - }, - "x-enum-varnames": [ - "ItemRatingLike", - "ItemRatingUnlike" - ] - }, - "conversation.ItemRole": { - "enum": [ - "system", - "user", - "assistant", - "tool", - "developer", - "critic", - "discriminator", - "unknown" - ], - "type": "string", - "x-enum-comments": { - "ItemRoleCritic": "For critique/evaluation workflows", - "ItemRoleDeveloper": "System-level instructions (OpenAI replacement for system)", - "ItemRoleDiscriminator": "For adversarial/validation workflows", - "ItemRoleUnknown": "Fallback for unrecognized roles" - }, - "x-enum-varnames": [ - "ItemRoleSystem", - "ItemRoleUser", - "ItemRoleAssistant", - "ItemRoleTool", - "ItemRoleDeveloper", - "ItemRoleCritic", - "ItemRoleDiscriminator", - "ItemRoleUnknown" - ] - }, - "conversation.ItemStatus": { - "enum": [ - "incomplete", - "in_progress", - "completed", - "failed", - "cancelled", - "searching", - "generating", - "calling", - "streaming", - "rate_limited" - ], - "type": "string", - "x-enum-comments": { - "ItemStatusCalling": "Function/tool call in progress", - "ItemStatusCancelled": "Cancelled by user or system", - "ItemStatusCompleted": "Successfully finished", - "ItemStatusFailed": "Failed with error", - "ItemStatusGenerating": "Image generation in progress", - "ItemStatusInProgress": "Currently processing", - "ItemStatusIncomplete": "Not started or partially complete (OpenAI uses this instead of \"pending\")", - "ItemStatusRateLimited": "Rate limit hit", - "ItemStatusSearching": "File/web search in progress", - "ItemStatusStreaming": "Streaming response in progress" - }, - "x-enum-varnames": [ - "ItemStatusIncomplete", - "ItemStatusInProgress", - "ItemStatusCompleted", - "ItemStatusFailed", - "ItemStatusCancelled", - "ItemStatusSearching", - "ItemStatusGenerating", - "ItemStatusCalling", - "ItemStatusStreaming", - "ItemStatusRateLimited" - ] - }, - "conversation.ItemType": { - "enum": [ - "message", - "function_call", - "function_call_output", - "reasoning", - "file_search_call", - "web_search_call", - "image_generation_call", - "image_edit_call", - "computer_call", - "computer_call_output", - "code_interpreter_call", - "local_shell_call", - "local_shell_call_output", - "shell_call", - "shell_call_output", - "apply_patch_call", - "apply_patch_call_output", - "mcp_list_tools", - "mcp_approval_request", - "mcp_approval_response", - "mcp_call", - "custom_tool_call", - "custom_tool_call_output", - "file_search", - "web_search", - "code_interpreter", - "computer_use", - "mcp_item", - "image_generation" - ], - "type": "string", - "x-enum-comments": { - "ItemTypeCodeInterpreter": "Legacy: maps to code_interpreter_call", - "ItemTypeComputerUse": "Legacy: maps to computer_call", - "ItemTypeFileSearch": "Legacy: maps to file_search_call", - "ItemTypeImageGeneration": "Legacy: maps to image_generation_call", - "ItemTypeMCPItem": "Legacy: maps to mcp_call", - "ItemTypeReasoning": "For o1/reasoning models", - "ItemTypeWebSearch": "Legacy: maps to web_search_call" - }, - "x-enum-varnames": [ - "ItemTypeMessage", - "ItemTypeFunctionCall", - "ItemTypeFunctionCallOut", - "ItemTypeReasoning", - "ItemTypeFileSearchCall", - "ItemTypeWebSearchCall", - "ItemTypeImageGenerationCall", - "ItemTypeImageEditCall", - "ItemTypeComputerCall", - "ItemTypeComputerCallOutput", - "ItemTypeCodeInterpreterCall", - "ItemTypeLocalShellCall", - "ItemTypeLocalShellCallOutput", - "ItemTypeShellCall", - "ItemTypeShellCallOutput", - "ItemTypeApplyPatchCall", - "ItemTypeApplyPatchCallOutput", - "ItemTypeMcpListTools", - "ItemTypeMcpApprovalRequest", - "ItemTypeMcpApprovalResponse", - "ItemTypeMcpCall", - "ItemTypeCustomToolCall", - "ItemTypeCustomToolCallOutput", - "ItemTypeFileSearch", - "ItemTypeWebSearch", - "ItemTypeCodeInterpreter", - "ItemTypeComputerUse", - "ItemTypeMCPItem", - "ItemTypeImageGeneration" - ] - }, - "conversation.LogProb": { - "properties": { - "bytes": { - "items": { - "type": "integer" - }, - "type": "array" - }, - "logprob": { - "type": "number" - }, - "token": { - "type": "string" - }, - "top_logprobs": { - "items": { - "$ref": "#/definitions/conversation.TopLogProb" - }, - "type": "array" - } - }, - "type": "object" - }, - "conversation.McpTool": { - "properties": { - "annotations": {}, - "description": { - "type": "string" - }, - "input_schema": {}, - "name": { - "type": "string" - } - }, - "type": "object" - }, - "conversation.OutputText": { - "properties": { - "annotations": { - "description": "Required for OpenAI compatibility", - "items": { - "$ref": "#/definitions/conversation.Annotation" - }, - "type": "array" - }, - "logprobs": { - "description": "Token probabilities", - "items": { - "$ref": "#/definitions/conversation.LogProb" - }, - "type": "array" - }, - "text": { - "type": "string" - } - }, - "type": "object" - }, - "conversation.SafetyCheck": { - "properties": { - "reason": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "type": "object" - }, - "conversation.ScreenshotContent": { - "properties": { - "description": { - "description": "Optional description", - "type": "string" - }, - "height": { - "description": "Image height in pixels", - "type": "integer" - }, - "image_data": { - "description": "Base64 encoded image data", - "type": "string" - }, - "image_url": { - "description": "URL to screenshot image", - "type": "string" - }, - "timestamp": { - "description": "Unix timestamp when screenshot was taken", - "type": "integer" - }, - "width": { - "description": "Image width in pixels", - "type": "integer" - } - }, - "type": "object" - }, - "conversation.ScrollDelta": { - "properties": { - "x": { - "type": "integer" - }, - "y": { - "type": "integer" - } - }, - "type": "object" - }, - "conversation.ShellOutput": { - "properties": { - "type": { - "description": "stdout, stderr, exit_code", - "type": "string" - }, - "value": { - "type": "string" - } - }, - "type": "object" - }, - "conversation.ToolCall": { - "properties": { - "function": { - "$ref": "#/definitions/conversation.FunctionCall" - }, - "id": { - "type": "string" - }, - "type": { - "description": "\"function\", \"file_search\", \"code_interpreter\"", - "type": "string" - } - }, - "type": "object" - }, - "conversation.TopLogProb": { - "properties": { - "bytes": { - "items": { - "type": "integer" - }, - "type": "array" - }, - "logprob": { - "type": "number" - }, - "token": { - "type": "string" - } - }, - "type": "object" - }, - "conversationhandler.ActivateBranchResponse": { - "properties": { - "active_branch": { - "type": "string" - }, - "message": { - "type": "string" - } - }, - "type": "object" - }, - "conversationhandler.BranchResponse": { - "properties": { - "created_at": { - "type": "integer" - }, - "description": { - "type": "string" - }, - "forked_at": { - "type": "integer" - }, - "forked_from_item_id": { - "type": "string" - }, - "is_active": { - "type": "boolean" - }, - "item_count": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "parent_branch": { - "type": "string" - }, - "updated_at": { - "type": "integer" - } - }, - "type": "object" - }, - "conversationhandler.CreateBranchRequest": { - "properties": { - "description": { - "type": "string" - }, - "fork_from_item_id": { - "type": "string" - }, - "name": { - "type": "string" - }, - "parent_branch": { - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "conversationhandler.DeleteItemResponse": { - "properties": { - "branch": { - "description": "Always \"MAIN\" after swap", - "type": "string" - }, - "branch_created": { - "type": "boolean" - }, - "deleted": { - "type": "boolean" - }, - "old_main_backup": { - "description": "Backup name for old MAIN", - "type": "string" - } - }, - "type": "object" - }, - "conversationhandler.EditMessageRequest": { - "properties": { - "content": { - "type": "string" - }, - "regenerate": { - "description": "Auto-trigger new response (default: true)", - "type": "boolean" - } - }, - "required": [ - "content" - ], - "type": "object" - }, - "conversationhandler.EditMessageResponse": { - "properties": { - "branch": { - "description": "Always \"MAIN\" after swap", - "type": "string" - }, - "branch_created": { - "type": "boolean" - }, - "new_branch": { - "$ref": "#/definitions/conversationhandler.BranchResponse" - }, - "old_main_backup": { - "description": "Backup name for old MAIN", - "type": "string" - }, - "user_item": { - "$ref": "#/definitions/conversation.Item" - } - }, - "type": "object" - }, - "conversationhandler.ListBranchesResponse": { - "properties": { - "active_branch": { - "type": "string" - }, - "data": { - "items": { - "$ref": "#/definitions/conversationhandler.BranchResponse" - }, - "type": "array" - }, - "object": { - "description": "\"list\"", - "type": "string" - } - }, - "type": "object" - }, - "conversationhandler.RegenerateMessageRequest": { - "properties": { - "max_tokens": { - "description": "Override max tokens", - "type": "integer" - }, - "model": { - "description": "Override model", - "type": "string" - }, - "temperature": { - "description": "Override temperature", - "type": "number" - } - }, - "type": "object" - }, - "conversationhandler.RegenerateMessageResponse": { - "properties": { - "branch": { - "description": "Always \"MAIN\" after swap", - "type": "string" - }, - "branch_created": { - "type": "boolean" - }, - "new_branch": { - "$ref": "#/definitions/conversationhandler.BranchResponse" - }, - "old_main_backup": { - "description": "Backup name for old MAIN", - "type": "string" - }, - "user_item_id": { - "type": "string" - } - }, - "type": "object" - }, - "conversationrequests.CreateConversationRequest": { - "properties": { - "items": { - "items": { - "$ref": "#/definitions/conversation.Item" - }, - "type": "array" - }, - "metadata": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - }, - "project_id": { - "type": "string" - }, - "referrer": { - "type": "string" - }, - "title": { - "type": "string" - } - }, - "type": "object" - }, - "conversationrequests.CreateItemsRequest": { - "properties": { - "items": { - "items": { - "$ref": "#/definitions/conversation.Item" - }, - "type": "array" - } - }, - "required": [ - "items" - ], - "type": "object" - }, - "conversationrequests.UpdateConversationRequest": { - "properties": { - "metadata": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - }, - "project_id": { - "type": "string" - }, - "referrer": { - "type": "string" - }, - "title": { - "type": "string" - } - }, - "type": "object" - }, - "conversationrequests.UpdateItemByCallIDRequest": { - "properties": { - "arguments": { - "description": "JSON string of arguments", - "type": "string" - }, - "error": { - "description": "Error message if status is \"failed\"", - "type": "string" - }, - "name": { - "description": "Tool info fields (optional - already set on creation, but can be updated)", - "type": "string" - }, - "output": { - "description": "Result fields", - "type": "string" - }, - "server_label": { - "description": "MCP server label", - "type": "string" - }, - "status": { - "description": "Required fields", - "type": "string" - } - }, - "required": [ - "status" - ], - "type": "object" - }, - "conversationresponses.BulkConversationsDeletedResponse": { - "properties": { - "deleted": { - "type": "boolean" - }, - "deleted_count": { - "type": "integer" - }, - "object": { - "type": "string" - } - }, - "type": "object" - }, - "conversationresponses.ConversationDeletedResponse": { - "properties": { - "deleted": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "object": { - "type": "string" - } - }, - "type": "object" - }, - "conversationresponses.ConversationItemCreatedResponse": { - "properties": { - "data": { - "items": { - "$ref": "#/definitions/conversation.Item" - }, - "type": "array" - }, - "first_id": { - "type": "string" - }, - "has_more": { - "type": "boolean" - }, - "last_id": { - "type": "string" - }, - "object": { - "type": "string" - } - }, - "type": "object" - }, - "conversationresponses.ConversationListResponse": { - "properties": { - "data": { - "items": { - "$ref": "#/definitions/conversationresponses.ConversationResponse" - }, - "type": "array" - }, - "first_id": { - "type": "string" - }, - "has_more": { - "type": "boolean" - }, - "last_id": { - "type": "string" - }, - "object": { - "type": "string" - }, - "total": { - "type": "integer" - } - }, - "type": "object" - }, - "conversationresponses.ConversationResponse": { - "properties": { - "created_at": { - "type": "integer" - }, - "id": { - "type": "string" - }, - "metadata": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - }, - "object": { - "type": "string" - }, - "project_id": { - "type": "string" - }, - "referrer": { - "type": "string" - }, - "title": { - "type": "string" - }, - "updated_at": { - "type": "integer" - } - }, - "type": "object" - }, - "conversationresponses.ItemListResponse": { - "properties": { - "data": { - "items": { - "$ref": "#/definitions/conversation.Item" - }, - "type": "array" - }, - "first_id": { - "type": "string" - }, - "has_more": { - "type": "boolean" - }, - "last_id": { - "type": "string" - }, - "object": { - "type": "string" - } - }, - "type": "object" - }, - "conversationresponses.ItemResponse": { - "properties": { - "acknowledged_safety_checks": { - "description": "For computer call outputs", - "items": { - "$ref": "#/definitions/conversation.SafetyCheck" - }, - "type": "array" - }, - "action": { - "additionalProperties": true, - "description": "For computer/shell actions", - "type": "object" - }, - "approval_request_id": { - "description": "For MCP approval", - "type": "string" - }, - "approve": { - "description": "For MCP approval response", - "type": "boolean" - }, - "arguments": { - "description": "For tool calls (JSON string)", - "type": "string" - }, - "branch": { - "description": "Branch identifier (MAIN, EDIT_1, etc.)", - "type": "string" - }, - "call_id": { - "description": "OpenAI-compatible fields for specific item types", - "type": "string" - }, - "commands": { - "description": "For shell calls", - "items": { - "type": "string" - }, - "type": "array" - }, - "completed_at": { - "type": "string" - }, - "content": { - "items": { - "$ref": "#/definitions/conversation.Content" - }, - "type": "array" - }, - "created_at": { - "type": "string" - }, - "error": { - "description": "For failed calls", - "type": "string" - }, - "id": { - "type": "string" - }, - "incomplete_at": { - "type": "string" - }, - "incomplete_details": { - "$ref": "#/definitions/conversation.IncompleteDetails" - }, - "max_output_length": { - "description": "For shell calls", - "type": "integer" - }, - "name": { - "description": "For MCP tool calls - tool name", - "type": "string" - }, - "object": { - "description": "Always \"conversation.item\" for OpenAI compatibility", - "type": "string" - }, - "operation": { - "additionalProperties": true, - "description": "For patch operations", - "type": "object" - }, - "output": { - "description": "For tool call outputs", - "type": "string" - }, - "pending_safety_checks": { - "description": "For computer calls", - "items": { - "$ref": "#/definitions/conversation.SafetyCheck" - }, - "type": "array" - }, - "rated_at": { - "description": "When rating was given", - "type": "string" - }, - "rating": { - "allOf": [ - { - "$ref": "#/definitions/conversation.ItemRating" - } - ], - "description": "User feedback/rating" - }, - "rating_comment": { - "description": "Optional comment with rating", - "type": "string" - }, - "reason": { - "description": "For MCP approval reason", - "type": "string" - }, - "role": { - "$ref": "#/definitions/conversation.ItemRole" - }, - "sequence_number": { - "description": "Order within branch", - "type": "integer" - }, - "server_label": { - "description": "For MCP calls", - "type": "string" - }, - "shell_outputs": { - "description": "For shell call outputs", - "items": { - "$ref": "#/definitions/conversation.ShellOutput" - }, - "type": "array" - }, - "status": { - "$ref": "#/definitions/conversation.ItemStatus" - }, - "tools": { - "description": "For mcp_list_tools", - "items": { - "$ref": "#/definitions/conversation.McpTool" - }, - "type": "array" - }, - "type": { - "$ref": "#/definitions/conversation.ItemType" - } - }, - "type": "object" - }, - "image.ImageData": { - "description": "Single generated image data", - "properties": { - "b64_json": { - "description": "B64JSON is the base64-encoded image data.\nPresent when response_format=\"b64_json\".", - "type": "string" - }, - "id": { - "description": "ID is the Jan media ID (jan_xxxxx) for the stored image.", - "example": "jan_abc123", - "type": "string" - }, - "revised_prompt": { - "description": "RevisedPrompt is the revised prompt used for generation, if the provider modified it.", - "type": "string" - }, - "url": { - "description": "URL is the presigned URL to the generated image.\nPresent when response_format=\"url\".", - "example": "https://media.jan.ai/images/jan_abc123.png?sig=...", - "type": "string" - } - }, - "type": "object" - }, - "image.ImageEditRequest": { - "properties": { - "cfg_scale": { - "description": "CfgScale is classifier-free guidance scale.", - "example": 1, - "type": "number" - }, - "conversation_id": { - "description": "ConversationID optionally links this edit to a conversation.", - "example": "conv_abc123", - "type": "string" - }, - "image": { - "allOf": [ - { - "$ref": "#/definitions/image.ImageInput" - } - ], - "description": "Image is the input image to edit. Required." - }, - "mask": { - "allOf": [ - { - "$ref": "#/definitions/image.ImageInput" - } - ], - "description": "Mask is an optional mask for inpainting." - }, - "model": { - "description": "Model specifies the image edit model. Optional.", - "example": "qwen-image-edit", - "type": "string" - }, - "n": { - "description": "N is the number of images to generate (only 1 supported by most providers).", - "example": 1, - "type": "integer" - }, - "negative_prompt": { - "description": "NegativePrompt describes what to avoid.", - "example": " ", - "type": "string" - }, - "prompt": { - "description": "Prompt is the text instruction describing the edit. Required.", - "example": "add golden sunglasses", - "type": "string" - }, - "provider_id": { - "description": "ProviderID optionally overrides the default image edit provider.", - "example": "prov_abc123", - "type": "string" - }, - "response_format": { - "description": "ResponseFormat determines output format (\"url\" or \"b64_json\").", - "example": "b64_json", - "type": "string" - }, - "sampler": { - "description": "Sampler selects the sampling algorithm.", - "example": "euler", - "type": "string" - }, - "scheduler": { - "description": "Scheduler selects the scheduler.", - "example": "simple", - "type": "string" - }, - "seed": { - "description": "Seed sets random seed (-1 for random).", - "example": -1, - "type": "integer" - }, - "size": { - "description": "Size specifies the output size (\"original\" or \"WIDTHxHEIGHT\").", - "example": "original", - "type": "string" - }, - "steps": { - "description": "Steps controls sampling steps.", - "example": 4, - "type": "integer" - }, - "store": { - "description": "Store controls whether to save the result to the conversation.\nnil/true = store (default), false = don't store.", - "example": true, - "type": "boolean" - }, - "strength": { - "description": "Strength controls edit intensity (0.0-1.0).", - "example": 1, - "type": "number" - } - }, - "required": [ - "image", - "prompt" - ], - "type": "object" - }, - "image.ImageGenerationRequest": { - "description": "OpenAI-compatible image generation request", - "properties": { - "cfg_scale": { - "description": "CfgScale is a provider-specific parameter (z-image/Flux) for guidance scale.", - "example": 7.5, - "type": "number" - }, - "conversation_id": { - "description": "ConversationID optionally links this generation to a conversation.", - "example": "conv_abc123", - "type": "string" - }, - "model": { - "description": "Model specifies the image generation model (e.g., \"z-image\", \"flux-dev\", \"dall-e-3\").\nIf omitted, defaults to the configured default model.", - "example": "z-image", - "type": "string" - }, - "n": { - "description": "N is the number of images to generate (1-10, default: 1).", - "example": 1, - "type": "integer" - }, - "num_inference_steps": { - "description": "NumInferenceSteps is a provider-specific parameter (z-image/Flux).", - "example": 20, - "type": "integer" - }, - "prompt": { - "description": "Prompt is the text description of the desired image. Required.", - "example": "A serene mountain landscape at sunset", - "type": "string" - }, - "provider_id": { - "description": "ProviderID optionally overrides the default image provider selection.", - "example": "prov_abc123", - "type": "string" - }, - "quality": { - "description": "Quality determines image quality. Valid values: \"standard\", \"hd\".\nDefault: \"standard\".", - "example": "standard", - "type": "string" - }, - "response_format": { - "description": "ResponseFormat determines output format. Valid values: \"url\", \"b64_json\".\nDefault: \"url\".", - "example": "url", - "type": "string" - }, - "size": { - "description": "Size specifies the dimensions of the generated image.\nSupported sizes: \"256x256\", \"512x512\", \"1024x1024\", \"1024x1792\", \"1792x1024\".\nDefault: \"1024x1024\".", - "example": "1024x1024", - "type": "string" - }, - "store": { - "description": "Store controls whether to save the result to the conversation.\nnil/true = store (default), false = don't store.", - "example": true, - "type": "boolean" - }, - "style": { - "description": "Style influences the visual aesthetic. Valid values: \"vivid\", \"natural\".\nDefault: \"natural\".", - "example": "natural", - "type": "string" - }, - "user": { - "description": "User is an optional unique identifier representing the end-user for abuse monitoring.", - "example": "user-123", - "type": "string" - } - }, - "required": [ - "prompt" - ], - "type": "object" - }, - "image.ImageGenerationResponse": { - "description": "OpenAI-compatible image generation response", - "properties": { - "created": { - "description": "Created is the Unix timestamp of when the response was generated.", - "example": 1699000000, - "type": "integer" - }, - "data": { - "description": "Data contains the generated images.", - "items": { - "$ref": "#/definitions/image.ImageData" - }, - "type": "array" - }, - "usage": { - "allOf": [ - { - "$ref": "#/definitions/image.ImageUsage" - } - ], - "description": "Usage contains token usage information for billing purposes." - } - }, - "type": "object" - }, - "image.ImageInput": { - "properties": { - "b64_json": { - "description": "B64JSON is base64-encoded image data (no data URL prefix).", - "type": "string" - }, - "id": { - "description": "ID is a Jan media ID (jan_*).", - "example": "jan_abc123", - "type": "string" - }, - "url": { - "description": "URL is a remote URL to the image.", - "example": "https://example.com/image.png", - "type": "string" - } - }, - "type": "object" - }, - "image.ImageUsage": { - "description": "Token usage information for billing", - "properties": { - "input_tokens": { - "description": "InputTokens is the estimated tokens for the prompt.", - "example": 100, - "type": "integer" - }, - "input_tokens_details": { - "allOf": [ - { - "$ref": "#/definitions/image.InputTokensDetail" - } - ], - "description": "InputTokensDetails provides a breakdown of input token types." - }, - "output_tokens": { - "description": "OutputTokens is the estimated tokens for the generated images.", - "example": 1400, - "type": "integer" - }, - "total_tokens": { - "description": "TotalTokens is the sum of input and output tokens.", - "example": 1500, - "type": "integer" - } - }, - "type": "object" - }, - "image.InputTokensDetail": { - "description": "Breakdown of input token types", - "properties": { - "image_tokens": { - "description": "ImageTokens is the number of tokens from input images (for edit/variation operations).", - "example": 0, - "type": "integer" - }, - "text_tokens": { - "description": "TextTokens is the number of tokens from the text prompt.", - "example": 100, - "type": "integer" - } - }, - "type": "object" - }, - "mcptool.UpdateMCPToolRequest": { - "properties": { - "category": { - "type": "string" - }, - "description": { - "type": "string" - }, - "disallowed_keywords": { - "description": "Regex patterns", - "items": { - "type": "string" - }, - "type": "array" - }, - "is_active": { - "type": "boolean" - }, - "metadata": { - "additionalProperties": {}, - "type": "object" - } - }, - "type": "object" - }, - "mcptoolhandler.ActiveToolsResponse": { - "properties": { - "data": { - "items": { - "$ref": "#/definitions/mcptoolhandler.MCPToolResponse" - }, - "type": "array" - } - }, - "type": "object" - }, - "mcptoolhandler.ListResponse": { - "properties": { - "data": { - "items": { - "$ref": "#/definitions/mcptoolhandler.MCPToolResponse" - }, - "type": "array" - }, - "total": { - "type": "integer" - } - }, - "type": "object" - }, - "mcptoolhandler.MCPToolResponse": { - "properties": { - "category": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "description": { - "type": "string" - }, - "disallowed_keywords": { - "items": { - "type": "string" - }, - "type": "array" - }, - "id": { - "type": "string" - }, - "is_active": { - "type": "boolean" - }, - "metadata": { - "additionalProperties": {}, - "type": "object" - }, - "name": { - "type": "string" - }, - "public_id": { - "type": "string" - }, - "tool_key": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - }, - "type": "object" - }, - "model.Architecture": { - "properties": { - "input_modalities": { - "items": { - "type": "string" - }, - "type": "array" - }, - "instruct_type": { - "description": "nullable", - "type": "string" - }, - "modality": { - "description": "\"text+image-\u003etext\"", - "type": "string" - }, - "output_modalities": { - "items": { - "type": "string" - }, - "type": "array" - }, - "tokenizer": { - "description": "\"GPT\" / \"SentencePiece\" / etc.", - "type": "string" - } - }, - "type": "object" - }, - "model.ModelCatalogStatus": { - "enum": [ - "init", - "filled", - "updated" - ], - "type": "string", - "x-enum-comments": { - "ModelCatalogStatusFilled": "may update from Provider like OpenRouter", - "ModelCatalogStatusInit": "default status when creating entry", - "ModelCatalogStatusUpdated": "manually updated by admin (cannot be auto-updated anymore" - }, - "x-enum-varnames": [ - "ModelCatalogStatusInit", - "ModelCatalogStatusFilled", - "ModelCatalogStatusUpdated" - ] - }, - "model.PriceLine": { - "properties": { - "amount_micro_usd": { - "description": "e.g., 15000 -\u003e $0.0150", - "type": "integer" - }, - "currency": { - "description": "\"USD\" (fixed if you only bill in USD)", - "type": "string" - }, - "unit": { - "$ref": "#/definitions/model.PriceUnit" - } - }, - "type": "object" - }, - "model.PriceUnit": { - "enum": [ - "per_1k_prompt_tokens", - "per_1k_completion_tokens", - "per_request", - "per_image", - "per_web_search", - "per_internal_reasoning" - ], - "type": "string", - "x-enum-varnames": [ - "Per1KPromptTokens", - "Per1KCompletionTokens", - "PerRequest", - "PerImage", - "PerWebSearch", - "PerInternalReasoning" - ] - }, - "model.Pricing": { - "properties": { - "lines": { - "description": "flexible: add/remove units without schema churn", - "items": { - "$ref": "#/definitions/model.PriceLine" - }, - "type": "array" - } - }, - "type": "object" - }, - "model.SupportedParameters": { - "properties": { - "default": { - "additionalProperties": { - "type": "number" - }, - "description": "temperature/top_p/frequency_penalty, null allowed", - "type": "object" - }, - "names": { - "description": "e.g., [\"include_reasoning\",\"max_tokens\",...]", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, - "model.TokenLimits": { - "properties": { - "context_length": { - "description": "e.g., 400000", - "type": "integer" - }, - "max_completion_tokens": { - "description": "e.g., 128000", - "type": "integer" - } - }, - "type": "object" - }, - "modelprompthandler.AssignRequest": { - "properties": { - "is_active": { - "type": "boolean" - }, - "priority": { - "type": "integer" - }, - "prompt_template_id": { - "type": "string" - }, - "template_key": { - "type": "string" - } - }, - "required": [ - "prompt_template_id", - "template_key" - ], - "type": "object" - }, - "modelprompthandler.EffectiveTemplateResponse": { - "properties": { - "source": { - "description": "\"model_specific\", \"global_default\", \"hardcoded\"", - "type": "string" - }, - "template": { - "$ref": "#/definitions/modelprompthandler.FullPromptTemplateResponse" - } - }, - "type": "object" - }, - "modelprompthandler.EffectiveTemplatesResponse": { - "properties": { - "templates": { - "additionalProperties": { - "$ref": "#/definitions/modelprompthandler.EffectiveTemplateResponse" - }, - "type": "object" - } - }, - "type": "object" - }, - "modelprompthandler.FullPromptTemplateResponse": { - "properties": { - "category": { - "type": "string" - }, - "content": { - "type": "string" - }, - "description": { - "type": "string" - }, - "is_active": { - "type": "boolean" - }, - "is_system": { - "type": "boolean" - }, - "metadata": { - "additionalProperties": {}, - "type": "object" - }, - "name": { - "type": "string" - }, - "public_id": { - "type": "string" - }, - "template_key": { - "type": "string" - }, - "variables": { - "items": { - "type": "string" - }, - "type": "array" - }, - "version": { - "type": "integer" - } - }, - "type": "object" - }, - "modelprompthandler.ListResponse": { - "properties": { - "data": { - "items": { - "$ref": "#/definitions/modelprompthandler.ModelPromptTemplateResponse" - }, - "type": "array" - }, - "total": { - "type": "integer" - } - }, - "type": "object" - }, - "modelprompthandler.ModelPromptTemplateResponse": { - "properties": { - "created_at": { - "type": "string" - }, - "id": { - "type": "string" - }, - "is_active": { - "type": "boolean" - }, - "model_catalog_id": { - "type": "string" - }, - "priority": { - "type": "integer" - }, - "prompt_template": { - "$ref": "#/definitions/modelprompthandler.PromptTemplateResponse" - }, - "prompt_template_id": { - "type": "string" - }, - "template_key": { - "type": "string" - }, - "updated_at": { - "type": "string" - } - }, - "type": "object" - }, - "modelprompthandler.PromptTemplateResponse": { - "properties": { - "category": { - "type": "string" - }, - "description": { - "type": "string" - }, - "is_active": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "public_id": { - "type": "string" - }, - "template_key": { - "type": "string" - } - }, - "type": "object" - }, - "modelprompthandler.UpdateRequest": { - "properties": { - "is_active": { - "type": "boolean" - }, - "priority": { - "type": "integer" - }, - "prompt_template_id": { - "type": "string" - } - }, - "type": "object" - }, - "modelresponses.BulkOperationResponse": { - "properties": { - "failed_count": { - "type": "integer" - }, - "failed_models": { - "items": { - "type": "string" - }, - "type": "array" - }, - "skipped_count": { - "type": "integer" - }, - "total_checked": { - "type": "integer" - }, - "updated_count": { - "type": "integer" - } - }, - "type": "object" - }, - "modelresponses.EndpointResponse": { - "properties": { - "healthy": { - "type": "boolean" - }, - "priority": { - "type": "integer" - }, - "url": { - "type": "string" - }, - "weight": { - "type": "integer" - } - }, - "type": "object" - }, - "modelresponses.ModelCatalogResponse": { - "properties": { - "active": { - "type": "boolean" - }, - "architecture": { - "$ref": "#/definitions/model.Architecture" - }, - "context_length": { - "type": "integer" - }, - "created_at": { - "type": "integer" - }, - "description": { - "type": "string" - }, - "experimental": { - "type": "boolean" - }, - "extras": { - "additionalProperties": {}, - "type": "object" - }, - "family": { - "type": "string" - }, - "id": { - "type": "string" - }, - "is_moderated": { - "type": "boolean" - }, - "last_synced_at": { - "type": "integer" - }, - "model_display_name": { - "type": "string" - }, - "notes": { - "type": "string" - }, - "public_id": { - "type": "string" - }, - "requires_feature_flag": { - "type": "string" - }, - "status": { - "$ref": "#/definitions/model.ModelCatalogStatus" - }, - "supported_parameters": { - "$ref": "#/definitions/model.SupportedParameters" - }, - "supports_audio": { - "type": "boolean" - }, - "supports_browser": { - "type": "boolean" - }, - "supports_embeddings": { - "type": "boolean" - }, - "supports_images": { - "type": "boolean" - }, - "supports_instruct": { - "type": "boolean" - }, - "supports_reasoning": { - "type": "boolean" - }, - "supports_tools": { - "type": "boolean" - }, - "supports_video": { - "type": "boolean" - }, - "tags": { - "items": { - "type": "string" - }, - "type": "array" - }, - "updated_at": { - "type": "integer" - } - }, - "type": "object" - }, - "modelresponses.ModelResponse": { - "properties": { - "category": { - "type": "string" - }, - "category_order_number": { - "type": "integer" - }, - "created": { - "type": "integer" - }, - "id": { - "type": "string" - }, - "model_display_name": { - "type": "string" - }, - "model_order_number": { - "type": "integer" - }, - "object": { - "type": "string" - }, - "owned_by": { - "type": "string" - } - }, - "type": "object" - }, - "modelresponses.ModelResponseList": { - "properties": { - "data": { - "items": { - "$ref": "#/definitions/modelresponses.ModelResponse" - }, - "type": "array" - }, - "object": { - "type": "string" - } - }, - "type": "object" - }, - "modelresponses.ModelResponseWithProvider": { - "properties": { - "category": { - "type": "string" - }, - "category_order_number": { - "type": "integer" - }, - "created": { - "type": "integer" - }, - "id": { - "type": "string" - }, - "model_display_name": { - "type": "string" - }, - "model_order_number": { - "type": "integer" - }, - "object": { - "type": "string" - }, - "owned_by": { - "type": "string" - }, - "provider_id": { - "type": "string" - }, - "provider_name": { - "type": "string" - }, - "provider_vendor": { - "type": "string" - } - }, - "type": "object" - }, - "modelresponses.ModelWithProviderResponseList": { - "properties": { - "data": { - "items": { - "$ref": "#/definitions/modelresponses.ModelResponseWithProvider" - }, - "type": "array" - }, - "object": { - "type": "string" - } - }, - "type": "object" - }, - "modelresponses.ProviderModelResponse": { - "properties": { - "active": { - "type": "boolean" - }, - "category": { - "type": "string" - }, - "category_order_number": { - "type": "integer" - }, - "created_at": { - "type": "integer" - }, - "family": { - "type": "string" - }, - "id": { - "type": "string" - }, - "instruct_model_public_id": { - "description": "Public ID of the instruct model to use when enable_thinking=false", - "type": "string" - }, - "model_catalog_id": { - "type": "string" - }, - "model_display_name": { - "type": "string" - }, - "model_order_number": { - "type": "integer" - }, - "model_public_id": { - "type": "string" - }, - "pricing": { - "$ref": "#/definitions/model.Pricing" - }, - "provider_id": { - "type": "string" - }, - "provider_original_model_id": { - "type": "string" - }, - "provider_vendor": { - "type": "string" - }, - "supports_audio": { - "type": "boolean" - }, - "supports_embeddings": { - "type": "boolean" - }, - "supports_images": { - "type": "boolean" - }, - "supports_instruct": { - "type": "boolean" - }, - "supports_reasoning": { - "type": "boolean" - }, - "supports_video": { - "type": "boolean" - }, - "token_limits": { - "$ref": "#/definitions/model.TokenLimits" - }, - "updated_at": { - "type": "integer" - } - }, - "type": "object" - }, - "modelresponses.ProviderResponse": { - "properties": { - "active": { - "type": "boolean" - }, - "base_url": { - "type": "string" - }, - "category": { - "type": "string" - }, - "default_provider_image_edit": { - "type": "boolean" - }, - "default_provider_image_generate": { - "type": "boolean" - }, - "endpoints": { - "items": { - "$ref": "#/definitions/modelresponses.EndpointResponse" - }, - "type": "array" - }, - "id": { - "type": "string" - }, - "metadata": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - }, - "name": { - "type": "string" - }, - "vendor": { - "type": "string" - } - }, - "type": "object" - }, - "modelresponses.ProviderResponseList": { - "properties": { - "data": { - "items": { - "$ref": "#/definitions/modelresponses.ProviderResponse" - }, - "type": "array" - }, - "object": { - "type": "string" - } - }, - "type": "object" - }, - "modelresponses.ProviderWithModelCountResponse": { - "properties": { - "active": { - "type": "boolean" - }, - "base_url": { - "type": "string" - }, - "category": { - "type": "string" - }, - "default_provider_image_edit": { - "type": "boolean" - }, - "default_provider_image_generate": { - "type": "boolean" - }, - "endpoints": { - "items": { - "$ref": "#/definitions/modelresponses.EndpointResponse" - }, - "type": "array" - }, - "id": { - "type": "string" - }, - "metadata": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - }, - "model_active_count": { - "type": "integer" - }, - "model_count": { - "type": "integer" - }, - "name": { - "type": "string" - }, - "vendor": { - "type": "string" - } - }, - "type": "object" - }, - "modelresponses.ProviderWithModelsResponse": { - "properties": { - "active": { - "type": "boolean" - }, - "base_url": { - "type": "string" - }, - "category": { - "type": "string" - }, - "default_provider_image_edit": { - "type": "boolean" - }, - "default_provider_image_generate": { - "type": "boolean" - }, - "endpoints": { - "items": { - "$ref": "#/definitions/modelresponses.EndpointResponse" - }, - "type": "array" - }, - "id": { - "type": "string" - }, - "metadata": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - }, - "models": { - "items": { - "$ref": "#/definitions/modelresponses.ModelResponse" - }, - "type": "array" - }, - "name": { - "type": "string" - }, - "vendor": { - "type": "string" - } - }, - "type": "object" - }, - "openai.ChatCompletionChoice": { - "properties": { - "content_filter_results": { - "$ref": "#/definitions/openai.ContentFilterResults" - }, - "finish_reason": { - "allOf": [ - { - "$ref": "#/definitions/openai.FinishReason" - } - ], - "description": "FinishReason\nstop: API returned complete message,\nor a message terminated by one of the stop sequences provided via the stop parameter\nlength: Incomplete model output due to max_tokens parameter or token limit\nfunction_call: The model decided to call a function\ncontent_filter: Omitted content due to a flag from our content filters\nnull: API response still in progress or incomplete" - }, - "index": { - "type": "integer" - }, - "logprobs": { - "$ref": "#/definitions/openai.LogProbs" - }, - "message": { - "$ref": "#/definitions/openai.ChatCompletionMessage" - } - }, - "type": "object" - }, - "openai.ChatCompletionMessage": { - "properties": { - "content": { - "type": "string" - }, - "function_call": { - "$ref": "#/definitions/openai.FunctionCall" - }, - "multiContent": { - "items": { - "$ref": "#/definitions/openai.ChatMessagePart" - }, - "type": "array" - }, - "name": { - "description": "This property isn't in the official documentation, but it's in\nthe documentation for the official library for python:\n- https://github.com/openai/openai-python/blob/main/chatml.md\n- https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb", - "type": "string" - }, - "reasoning_content": { - "description": "This property is used for the \"reasoning\" feature supported by deepseek-reasoner\nwhich is not in the official documentation.\nthe doc from deepseek:\n- https://api-docs.deepseek.com/api/create-chat-completion#responses", - "type": "string" - }, - "refusal": { - "type": "string" - }, - "role": { - "type": "string" - }, - "tool_call_id": { - "description": "For Role=tool prompts this should be set to the ID given in the assistant's prior request to call a tool.", - "type": "string" - }, - "tool_calls": { - "description": "For Role=assistant prompts this may be set to the tool calls generated by the model, such as function calls.", - "items": { - "$ref": "#/definitions/openai.ToolCall" - }, - "type": "array" - } - }, - "type": "object" - }, - "openai.ChatCompletionResponseFormat": { - "properties": { - "json_schema": { - "$ref": "#/definitions/openai.ChatCompletionResponseFormatJSONSchema" - }, - "type": { - "$ref": "#/definitions/openai.ChatCompletionResponseFormatType" - } - }, - "type": "object" - }, - "openai.ChatCompletionResponseFormatJSONSchema": { - "properties": { - "description": { - "type": "string" - }, - "name": { - "type": "string" - }, - "schema": {}, - "strict": { - "type": "boolean" - } - }, - "type": "object" - }, - "openai.ChatCompletionResponseFormatType": { - "enum": [ - "json_object", - "json_schema", - "text" - ], - "type": "string", - "x-enum-varnames": [ - "ChatCompletionResponseFormatTypeJSONObject", - "ChatCompletionResponseFormatTypeJSONSchema", - "ChatCompletionResponseFormatTypeText" - ] - }, - "openai.ChatMessageImageURL": { - "properties": { - "detail": { - "$ref": "#/definitions/openai.ImageURLDetail" - }, - "url": { - "type": "string" - } - }, - "type": "object" - }, - "openai.ChatMessagePart": { - "properties": { - "image_url": { - "$ref": "#/definitions/openai.ChatMessageImageURL" - }, - "text": { - "type": "string" - }, - "type": { - "$ref": "#/definitions/openai.ChatMessagePartType" - } - }, - "type": "object" - }, - "openai.ChatMessagePartType": { - "enum": [ - "text", - "image_url" - ], - "type": "string", - "x-enum-varnames": [ - "ChatMessagePartTypeText", - "ChatMessagePartTypeImageURL" - ] - }, - "openai.CompletionTokensDetails": { - "properties": { - "accepted_prediction_tokens": { - "type": "integer" - }, - "audio_tokens": { - "type": "integer" - }, - "reasoning_tokens": { - "type": "integer" - }, - "rejected_prediction_tokens": { - "type": "integer" - } - }, - "type": "object" - }, - "openai.ContentFilterResults": { - "properties": { - "hate": { - "$ref": "#/definitions/openai.Hate" - }, - "jailbreak": { - "$ref": "#/definitions/openai.JailBreak" - }, - "profanity": { - "$ref": "#/definitions/openai.Profanity" - }, - "self_harm": { - "$ref": "#/definitions/openai.SelfHarm" - }, - "sexual": { - "$ref": "#/definitions/openai.Sexual" - }, - "violence": { - "$ref": "#/definitions/openai.Violence" - } - }, - "type": "object" - }, - "openai.FinishReason": { - "enum": [ - "stop", - "length", - "function_call", - "tool_calls", - "content_filter", - "null" - ], - "type": "string", - "x-enum-varnames": [ - "FinishReasonStop", - "FinishReasonLength", - "FinishReasonFunctionCall", - "FinishReasonToolCalls", - "FinishReasonContentFilter", - "FinishReasonNull" - ] - }, - "openai.FunctionCall": { - "properties": { - "arguments": { - "description": "call function with arguments in JSON format", - "type": "string" - }, - "name": { - "type": "string" - } - }, - "type": "object" - }, - "openai.FunctionDefinition": { - "properties": { - "description": { - "type": "string" - }, - "name": { - "type": "string" - }, - "parameters": { - "description": "Parameters is an object describing the function.\nYou can pass json.RawMessage to describe the schema,\nor you can pass in a struct which serializes to the proper JSON schema.\nThe jsonschema package is provided for convenience, but you should\nconsider another specialized library if you require more complex schemas." - }, - "strict": { - "type": "boolean" - } - }, - "type": "object" - }, - "openai.Hate": { - "properties": { - "filtered": { - "type": "boolean" - }, - "severity": { - "type": "string" - } - }, - "type": "object" - }, - "openai.ImageURLDetail": { - "enum": [ - "high", - "low", - "auto" - ], - "type": "string", - "x-enum-varnames": [ - "ImageURLDetailHigh", - "ImageURLDetailLow", - "ImageURLDetailAuto" - ] - }, - "openai.JailBreak": { - "properties": { - "detected": { - "type": "boolean" - }, - "filtered": { - "type": "boolean" - } - }, - "type": "object" - }, - "openai.LogProb": { - "properties": { - "bytes": { - "description": "Omitting the field if it is null", - "items": { - "type": "integer" - }, - "type": "array" - }, - "logprob": { - "type": "number" - }, - "token": { - "type": "string" - }, - "top_logprobs": { - "description": "TopLogProbs is a list of the most likely tokens and their log probability, at this token position.\nIn rare cases, there may be fewer than the number of requested top_logprobs returned.", - "items": { - "$ref": "#/definitions/openai.TopLogProbs" - }, - "type": "array" - } - }, - "type": "object" - }, - "openai.LogProbs": { - "properties": { - "content": { - "description": "Content is a list of message content tokens with log probability information.", - "items": { - "$ref": "#/definitions/openai.LogProb" - }, - "type": "array" - } - }, - "type": "object" - }, - "openai.Prediction": { - "properties": { - "content": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "type": "object" - }, - "openai.Profanity": { - "properties": { - "detected": { - "type": "boolean" - }, - "filtered": { - "type": "boolean" - } - }, - "type": "object" - }, - "openai.PromptFilterResult": { - "properties": { - "content_filter_results": { - "$ref": "#/definitions/openai.ContentFilterResults" - }, - "index": { - "type": "integer" - } - }, - "type": "object" - }, - "openai.PromptTokensDetails": { - "properties": { - "audio_tokens": { - "type": "integer" - }, - "cached_tokens": { - "type": "integer" - } - }, - "type": "object" - }, - "openai.SelfHarm": { - "properties": { - "filtered": { - "type": "boolean" - }, - "severity": { - "type": "string" - } - }, - "type": "object" - }, - "openai.ServiceTier": { - "enum": [ - "auto", - "default", - "flex", - "priority" - ], - "type": "string", - "x-enum-varnames": [ - "ServiceTierAuto", - "ServiceTierDefault", - "ServiceTierFlex", - "ServiceTierPriority" - ] - }, - "openai.Sexual": { - "properties": { - "filtered": { - "type": "boolean" - }, - "severity": { - "type": "string" - } - }, - "type": "object" - }, - "openai.StreamOptions": { - "properties": { - "include_usage": { - "description": "If set, an additional chunk will be streamed before the data: [DONE] message.\nThe usage field on this chunk shows the token usage statistics for the entire request,\nand the choices field will always be an empty array.\nAll other chunks will also include a usage field, but with a null value.", - "type": "boolean" - } - }, - "type": "object" - }, - "openai.Tool": { - "properties": { - "function": { - "$ref": "#/definitions/openai.FunctionDefinition" - }, - "type": { - "$ref": "#/definitions/openai.ToolType" - } - }, - "type": "object" - }, - "openai.ToolCall": { - "properties": { - "function": { - "$ref": "#/definitions/openai.FunctionCall" - }, - "id": { - "type": "string" - }, - "index": { - "description": "Index is not nil only in chat completion chunk object", - "type": "integer" - }, - "type": { - "$ref": "#/definitions/openai.ToolType" - } - }, - "type": "object" - }, - "openai.ToolType": { - "enum": [ - "function" - ], - "type": "string", - "x-enum-varnames": [ - "ToolTypeFunction" - ] - }, - "openai.TopLogProbs": { - "properties": { - "bytes": { - "items": { - "type": "integer" - }, - "type": "array" - }, - "logprob": { - "type": "number" - }, - "token": { - "type": "string" - } - }, - "type": "object" - }, - "openai.Usage": { - "properties": { - "completion_tokens": { - "type": "integer" - }, - "completion_tokens_details": { - "$ref": "#/definitions/openai.CompletionTokensDetails" - }, - "prompt_tokens": { - "type": "integer" - }, - "prompt_tokens_details": { - "$ref": "#/definitions/openai.PromptTokensDetails" - }, - "total_tokens": { - "type": "integer" - } - }, - "type": "object" - }, - "openai.Violence": { - "properties": { - "filtered": { - "type": "boolean" - }, - "severity": { - "type": "string" - } - }, - "type": "object" - }, - "projectreq.CreateProjectRequest": { - "properties": { - "instruction": { - "type": "string" - }, - "name": { - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "projectreq.UpdateProjectRequest": { - "properties": { - "instruction": { - "type": "string" - }, - "is_archived": { - "type": "boolean" - }, - "is_favorite": { - "type": "boolean" - }, - "name": { - "type": "string" - } - }, - "type": "object" - }, - "projectres.ProjectDeletedResponse": { - "properties": { - "deleted": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "object": { - "type": "string" - } - }, - "type": "object" - }, - "projectres.ProjectListResponse": { - "properties": { - "data": { - "items": { - "$ref": "#/definitions/projectres.ProjectResponse" - }, - "type": "array" - }, - "first_id": { - "type": "string" - }, - "has_more": { - "type": "boolean" - }, - "last_id": { - "type": "string" - }, - "next_cursor": { - "type": "string" - }, - "object": { - "type": "string" - }, - "total": { - "type": "integer" - } - }, - "type": "object" - }, - "projectres.ProjectResponse": { - "properties": { - "archived_at": { - "type": "integer" - }, - "created_at": { - "type": "integer" - }, - "id": { - "type": "string" - }, - "instruction": { - "type": "string" - }, - "is_archived": { - "type": "boolean" - }, - "is_favorite": { - "type": "boolean" - }, - "name": { - "type": "string" - }, - "object": { - "type": "string" - }, - "updated_at": { - "type": "integer" - } - }, - "type": "object" - }, - "prompttemplate.CreatePromptTemplateRequest": { - "properties": { - "category": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "content": { - "minLength": 1, - "type": "string" - }, - "description": { - "type": "string" - }, - "metadata": { - "additionalProperties": {}, - "type": "object" - }, - "name": { - "maxLength": 255, - "minLength": 1, - "type": "string" - }, - "template_key": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "variables": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "category", - "content", - "name", - "template_key" - ], - "type": "object" - }, - "prompttemplate.UpdatePromptTemplateRequest": { - "properties": { - "category": { - "maxLength": 100, - "minLength": 1, - "type": "string" - }, - "content": { - "minLength": 1, - "type": "string" - }, - "description": { - "type": "string" - }, - "is_active": { - "type": "boolean" - }, - "metadata": { - "additionalProperties": {}, - "type": "object" - }, - "name": { - "maxLength": 255, - "minLength": 1, - "type": "string" - }, - "variables": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, - "prompttemplatehandler.DuplicateRequest": { - "properties": { - "new_name": { - "maxLength": 200, - "type": "string" - } - }, - "type": "object" - }, - "prompttemplatehandler.ListResponse": { - "properties": { - "data": { - "items": { - "$ref": "#/definitions/prompttemplatehandler.PromptTemplateResponse" - }, - "type": "array" - }, - "total": { - "type": "integer" - } - }, - "type": "object" - }, - "prompttemplatehandler.PromptTemplateResponse": { - "properties": { - "category": { - "type": "string" - }, - "content": { - "type": "string" - }, - "created_at": { - "type": "string" - }, - "description": { - "type": "string" - }, - "id": { - "type": "string" - }, - "is_active": { - "type": "boolean" - }, - "is_system": { - "type": "boolean" - }, - "metadata": { - "additionalProperties": {}, - "type": "object" - }, - "name": { - "type": "string" - }, - "public_id": { - "type": "string" - }, - "template_key": { - "type": "string" - }, - "updated_at": { - "type": "string" - }, - "variables": { - "items": { - "type": "string" - }, - "type": "array" - }, - "version": { - "type": "integer" - } - }, - "type": "object" - }, - "requestmodels.AddProviderRequest": { - "properties": { - "active": { - "type": "boolean" - }, - "api_key": { - "type": "string" - }, - "base_url": { - "type": "string" - }, - "category": { - "description": "\"llm\" or \"image\", defaults to \"llm\"", - "type": "string" - }, - "default_provider_image_edit": { - "type": "boolean" - }, - "default_provider_image_generate": { - "type": "boolean" - }, - "endpoints": { - "items": { - "$ref": "#/definitions/requestmodels.EndpointDTO" - }, - "type": "array" - }, - "metadata": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - }, - "name": { - "type": "string" - }, - "url": { - "type": "string" - }, - "vendor": { - "type": "string" - } - }, - "required": [ - "name", - "vendor" - ], - "type": "object" - }, - "requestmodels.BulkEnableModelsRequest": { - "properties": { - "enable": { - "description": "Required: true to enable, false to disable", - "type": "boolean" - }, - "except_models": { - "description": "List of model keys to exclude", - "items": { - "type": "string" - }, - "type": "array" - }, - "provider_id": { - "description": "Optional: filter by provider", - "minLength": 1, - "type": "string" - } - }, - "required": [ - "enable" - ], - "type": "object" - }, - "requestmodels.BulkToggleCatalogsRequest": { - "properties": { - "catalog_ids": { - "description": "Optional: specific catalog public IDs. If empty, applies to all catalogs", - "items": { - "type": "string" - }, - "type": "array" - }, - "enable": { - "description": "Required: true to enable, false to disable", - "type": "boolean" - }, - "except_models": { - "description": "List of model keys to exclude from the operation", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "enable" - ], - "type": "object" - }, - "requestmodels.EndpointDTO": { - "properties": { - "priority": { - "type": "integer" - }, - "url": { - "type": "string" - }, - "weight": { - "type": "integer" - } - }, - "type": "object" - }, - "requestmodels.UpdateModelCatalogRequest": { - "properties": { - "active": { - "type": "boolean" - }, - "architecture": { - "$ref": "#/definitions/model.Architecture" - }, - "context_length": { - "type": "number" - }, - "description": { - "type": "string" - }, - "experimental": { - "type": "boolean" - }, - "extras": { - "additionalProperties": {}, - "type": "object" - }, - "family": { - "type": "string" - }, - "is_moderated": { - "type": "boolean" - }, - "model_display_name": { - "type": "string" - }, - "notes": { - "type": "string" - }, - "requires_feature_flag": { - "type": "string" - }, - "supported_parameters": { - "$ref": "#/definitions/model.SupportedParameters" - }, - "supports_audio": { - "type": "boolean" - }, - "supports_browser": { - "type": "boolean" - }, - "supports_embeddings": { - "type": "boolean" - }, - "supports_images": { - "type": "boolean" - }, - "supports_instruct": { - "type": "boolean" - }, - "supports_reasoning": { - "type": "boolean" - }, - "supports_tools": { - "type": "boolean" - }, - "supports_video": { - "type": "boolean" - }, - "tags": { - "items": { - "type": "string" - }, - "type": "array" - } - }, - "type": "object" - }, - "requestmodels.UpdateProviderModelRequest": { - "properties": { - "active": { - "type": "boolean" - }, - "category": { - "type": "string" - }, - "category_order_number": { - "type": "integer" - }, - "family": { - "type": "string" - }, - "instruct_model_public_id": { - "description": "Public ID of the instruct model to use when enable_thinking=false", - "type": "string" - }, - "model_display_name": { - "type": "string" - }, - "model_order_number": { - "type": "integer" - }, - "pricing": { - "$ref": "#/definitions/model.Pricing" - }, - "supports_audio": { - "type": "boolean" - }, - "supports_embeddings": { - "type": "boolean" - }, - "supports_images": { - "type": "boolean" - }, - "supports_reasoning": { - "type": "boolean" - }, - "supports_video": { - "type": "boolean" - }, - "token_limits": { - "$ref": "#/definitions/model.TokenLimits" - } - }, - "type": "object" - }, - "requestmodels.UpdateProviderRequest": { - "properties": { - "active": { - "type": "boolean" - }, - "api_key": { - "type": "string" - }, - "base_url": { - "type": "string" - }, - "default_provider_image_edit": { - "type": "boolean" - }, - "default_provider_image_generate": { - "type": "boolean" - }, - "endpoints": { - "items": { - "$ref": "#/definitions/requestmodels.EndpointDTO" - }, - "type": "array" - }, - "metadata": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - }, - "name": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "type": "object" - }, - "responses.ErrorResponse": { - "properties": { - "code": { - "description": "UUID from PlatformError", - "type": "string" - }, - "error": { - "type": "string" - }, - "message": { - "type": "string" - }, - "request_id": { - "type": "string" - } - }, - "type": "object" - }, - "sharerequests.CreateShareRequest": { - "properties": { - "branch": { - "description": "Branch to share from (defaults to active branch)", - "type": "string" - }, - "include_context_messages": { - "description": "For single-message share", - "type": "boolean" - }, - "include_images": { - "type": "boolean" - }, - "item_id": { - "description": "Required if scope is \"item\"", - "type": "string" - }, - "scope": { - "description": "\"conversation\" or \"item\"", - "enum": [ - "conversation", - "item" - ], - "type": "string" - }, - "title": { - "type": "string" - } - }, - "required": [ - "scope" - ], - "type": "object" - }, - "shareresponses.AnnotationResp": { - "properties": { - "end_index": { - "type": "integer" - }, - "file_id": { - "type": "string" - }, - "start_index": { - "type": "integer" - }, - "text": { - "type": "string" - }, - "type": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "type": "object" - }, - "shareresponses.FileRefResp": { - "properties": { - "file_id": { - "type": "string" - }, - "mime_type": { - "type": "string" - }, - "name": { - "type": "string" - }, - "url": { - "description": "For data URLs or external image URLs", - "type": "string" - } - }, - "type": "object" - }, - "shareresponses.ImageRefResp": { - "properties": { - "detail": { - "type": "string" - }, - "file_id": { - "type": "string" - }, - "url": { - "type": "string" - } - }, - "type": "object" - }, - "shareresponses.PublicShareResponse": { - "properties": { - "created_at": { - "type": "integer" - }, - "object": { - "type": "string" - }, - "slug": { - "type": "string" - }, - "snapshot": { - "$ref": "#/definitions/shareresponses.SnapshotResp" - }, - "title": { - "type": "string" - } - }, - "type": "object" - }, - "shareresponses.ShareDeletedResponse": { - "properties": { - "deleted": { - "type": "boolean" - }, - "id": { - "type": "string" - }, - "object": { - "type": "string" - } - }, - "type": "object" - }, - "shareresponses.ShareListResponse": { - "properties": { - "data": { - "items": { - "$ref": "#/definitions/shareresponses.ShareResponse" - }, - "type": "array" - }, - "object": { - "type": "string" - } - }, - "type": "object" - }, - "shareresponses.ShareOptionsResp": { - "properties": { - "include_context_messages": { - "type": "boolean" - }, - "include_images": { - "type": "boolean" - } - }, - "type": "object" - }, - "shareresponses.ShareResponse": { - "properties": { - "created_at": { - "type": "integer" - }, - "id": { - "type": "string" - }, - "item_id": { - "type": "string" - }, - "last_viewed_at": { - "type": "integer" - }, - "object": { - "type": "string" - }, - "revoked_at": { - "type": "integer" - }, - "share_options": { - "$ref": "#/definitions/shareresponses.ShareOptionsResp" - }, - "share_url": { - "type": "string" - }, - "slug": { - "type": "string" - }, - "snapshot_version": { - "type": "integer" - }, - "title": { - "type": "string" - }, - "updated_at": { - "type": "integer" - }, - "view_count": { - "type": "integer" - }, - "visibility": { - "type": "string" - } - }, - "type": "object" - }, - "shareresponses.SnapshotContentResp": { - "properties": { - "annotations": { - "items": { - "$ref": "#/definitions/shareresponses.AnnotationResp" - }, - "type": "array" - }, - "file_ref": { - "$ref": "#/definitions/shareresponses.FileRefResp" - }, - "image": { - "$ref": "#/definitions/shareresponses.ImageRefResp" - }, - "input_text": { - "type": "string" - }, - "output_text": { - "type": "string" - }, - "text": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "type": "object" - }, - "shareresponses.SnapshotItemResp": { - "properties": { - "content": { - "items": { - "$ref": "#/definitions/shareresponses.SnapshotContentResp" - }, - "type": "array" - }, - "created_at": { - "type": "integer" - }, - "id": { - "type": "string" - }, - "role": { - "type": "string" - }, - "type": { - "type": "string" - } - }, - "type": "object" - }, - "shareresponses.SnapshotResp": { - "properties": { - "assistant_name": { - "type": "string" - }, - "created_at": { - "type": "integer" - }, - "items": { - "items": { - "$ref": "#/definitions/shareresponses.SnapshotItemResp" - }, - "type": "array" - }, - "model_name": { - "type": "string" - }, - "title": { - "type": "string" - } - }, - "type": "object" - }, - "tokenusage.DailyAggregate": { - "properties": { - "date": { - "type": "string" - }, - "estimated_cost_usd": { - "type": "number" - }, - "request_count": { - "type": "integer" - }, - "total_completion_tokens": { - "type": "integer" - }, - "total_prompt_tokens": { - "type": "integer" - }, - "total_tokens": { - "type": "integer" - } - }, - "type": "object" - }, - "tokenusage.Period": { - "properties": { - "end_date": { - "type": "string" - }, - "start_date": { - "type": "string" - } - }, - "type": "object" - }, - "tokenusage.PlatformUsageResponse": { - "properties": { - "by_model": { - "items": { - "$ref": "#/definitions/tokenusage.UsageSummary" - }, - "type": "array" - }, - "by_provider": { - "items": { - "$ref": "#/definitions/tokenusage.UsageSummary" - }, - "type": "array" - }, - "period": { - "$ref": "#/definitions/tokenusage.Period" - }, - "top_users": { - "items": { - "$ref": "#/definitions/tokenusage.UserUsage" - }, - "type": "array" - }, - "total_usage": { - "$ref": "#/definitions/tokenusage.UsageSummary" - } - }, - "type": "object" - }, - "tokenusage.UsageResponse": { - "properties": { - "by_model": { - "items": { - "$ref": "#/definitions/tokenusage.UsageSummary" - }, - "type": "array" - }, - "by_provider": { - "items": { - "$ref": "#/definitions/tokenusage.UsageSummary" - }, - "type": "array" - }, - "period": { - "$ref": "#/definitions/tokenusage.Period" - }, - "total_usage": { - "$ref": "#/definitions/tokenusage.UsageSummary" - } - }, - "type": "object" - }, - "tokenusage.UsageSummary": { - "properties": { - "estimated_cost_usd": { - "type": "number" - }, - "model": { - "type": "string" - }, - "provider": { - "type": "string" - }, - "request_count": { - "type": "integer" - }, - "total_completion_tokens": { - "type": "integer" - }, - "total_prompt_tokens": { - "type": "integer" - }, - "total_tokens": { - "type": "integer" - } - }, - "type": "object" - }, - "tokenusage.UserUsage": { - "properties": { - "estimated_cost_usd": { - "type": "number" - }, - "request_count": { - "type": "integer" - }, - "total_completion_tokens": { - "type": "integer" - }, - "total_prompt_tokens": { - "type": "integer" - }, - "total_tokens": { - "type": "integer" - }, - "user_id": { - "type": "string" - } - }, - "type": "object" - }, - "usersettings.AdvancedSettings": { - "properties": { - "code_enabled": { - "description": "Enable code execution features", - "type": "boolean" - }, - "web_search": { - "description": "Let Jan automatically search the web for answers", - "type": "boolean" - } - }, - "type": "object" - }, - "usersettings.BaseStyle": { - "enum": [ - "Concise", - "Friendly", - "Professional" - ], - "type": "string", - "x-enum-varnames": [ - "BaseStyleConcise", - "BaseStyleFriendly", - "BaseStyleProfessional" - ] - }, - "usersettings.MemoryConfig": { - "properties": { - "enabled": { - "type": "boolean" - }, - "inject_episodic": { - "type": "boolean" - }, - "inject_semantic": { - "type": "boolean" - }, - "inject_user_core": { - "type": "boolean" - }, - "max_episodic_items": { - "type": "integer" - }, - "max_project_items": { - "type": "integer" - }, - "max_user_items": { - "type": "integer" - }, - "min_similarity": { - "type": "number" - }, - "observe_enabled": { - "type": "boolean" - } - }, - "type": "object" - }, - "usersettings.ProfileSettings": { - "properties": { - "base_style": { - "allOf": [ - { - "$ref": "#/definitions/usersettings.BaseStyle" - } - ], - "description": "Conversation style: Concise, Friendly, or Professional" - }, - "custom_instructions": { - "description": "Additional behavior, style, and tone preferences", - "type": "string" - }, - "more_about_you": { - "description": "Additional information about the user", - "type": "string" - }, - "nick_name": { - "description": "What should Jan call you? (alias: nickname)", - "type": "string" - }, - "occupation": { - "description": "User's occupation", - "type": "string" - } - }, - "type": "object" - }, - "usersettings.UpdateRequest": { - "properties": { - "advanced_settings": { - "$ref": "#/definitions/usersettings.AdvancedSettings" - }, - "enable_tools": { - "type": "boolean" - }, - "enable_trace": { - "type": "boolean" - }, - "memory_config": { - "$ref": "#/definitions/usersettings.MemoryConfig" - }, - "preferences": { - "additionalProperties": true, - "type": "object" - }, - "profile_settings": { - "$ref": "#/definitions/usersettings.ProfileSettings" - } - }, - "type": "object" - }, - "usersettingshandler.PreferencesResponse": { - "properties": { - "preferences": { - "additionalProperties": true, - "type": "object" - } - }, - "type": "object" - }, - "usersettingshandler.ServerCapabilities": { - "properties": { - "image_generation_enabled": { - "type": "boolean" - } - }, - "type": "object" - }, - "usersettingshandler.UpdatePreferencesRequest": { - "properties": { - "preferences": { - "additionalProperties": true, - "type": "object" - } - }, - "type": "object" - }, - "usersettingshandler.UserSettingsResponse": { - "properties": { - "advanced_settings": { - "$ref": "#/definitions/usersettings.AdvancedSettings" - }, - "created_at": { - "type": "string" - }, - "enable_tools": { - "type": "boolean" - }, - "enable_trace": { - "type": "boolean" - }, - "id": { - "type": "integer" - }, - "memory_config": { - "$ref": "#/definitions/usersettings.MemoryConfig" - }, - "preferences": { - "additionalProperties": true, - "type": "object" - }, - "profile_settings": { - "$ref": "#/definitions/usersettings.ProfileSettings" - }, - "server_capabilities": { - "$ref": "#/definitions/usersettingshandler.ServerCapabilities" - }, - "updated_at": { - "type": "string" - }, - "user_id": { - "type": "integer" - } - }, - "type": "object" - } - }, - "info": { - "contact": { - "name": "Jan Server Team", - "url": "https://github.com/janhq/jan-server" - }, - "description": "Unified API documentation for Jan Server including LLM API (OpenAI-compatible), MCP Tools, and Realtime API", - "title": "Jan Server API (LLM API + MCP Tools + Realtime API)", - "version": "2.0" - }, - "paths": { - "/auth/api-keys": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Returns all API keys created by the authenticated user. Key values are not returned, only metadata.", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "List of API keys with metadata", - "schema": { - "type": "object" - } - }, - "401": { - "description": "Unauthorized - invalid or expired token", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "List user's API keys", - "tags": [ - "Authentication API" - ] - }, - "post": { - "consumes": [ - "application/json" - ], - "description": "Creates a new API key for the authenticated user. API keys provide programmatic access without requiring user credentials.", - "parameters": [ - { - "description": "API key creation request with name and optional scopes", - "in": "body", - "name": "request", - "required": true, - "schema": { - "type": "object" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "201": { - "description": "API key created successfully with key value", - "schema": { - "type": "object" - } - }, - "400": { - "description": "Invalid request - missing required fields", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - invalid or expired token", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Create API key", - "tags": [ - "Authentication API" - ] - } - }, - "/auth/api-keys/{id}": { - "delete": { - "consumes": [ - "application/json" - ], - "description": "Revokes and deletes an API key by ID. Deleted keys can no longer be used for authentication.", - "parameters": [ - { - "description": "API key ID", - "in": "path", - "name": "id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "204": { - "description": "API key deleted successfully" - }, - "401": { - "description": "Unauthorized - invalid or expired token", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "API key not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Delete API key", - "tags": [ - "Authentication API" - ] - } - }, - "/auth/callback": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Handles the OAuth2 callback from Keycloak, exchanges authorization code for JWT tokens", - "parameters": [ - { - "description": "Authorization code from Keycloak", - "in": "query", - "name": "code", - "required": true, - "type": "string" - }, - { - "description": "State parameter for CSRF protection", - "in": "query", - "name": "state", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "JWT tokens", - "schema": { - "properties": { - "access_token": { - "type": "string" - }, - "expires_in": { - "type": "integer" - }, - "refresh_token": { - "type": "string" - }, - "token_type": { - "type": "string" - } - }, - "type": "object" - } - }, - "400": { - "description": "Missing code or state", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Invalid state parameter", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Failed to exchange code for tokens", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "summary": "Handle Keycloak OAuth2 callback", - "tags": [ - "Authentication API" - ] - } - }, - "/auth/guest-login": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Creates a temporary guest user account and returns JWT tokens. Guest users have limited access and can be upgraded to full accounts later.", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Guest user created with access and refresh tokens", - "schema": { - "type": "object" - } - }, - "500": { - "description": "Internal server error - failed to create guest user", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "summary": "Create guest user account", - "tags": [ - "Authentication API" - ] - } - }, - "/auth/keycloak/callback": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Handles the OAuth2 callback from Keycloak, exchanges authorization code for tokens using PKCE", - "parameters": [ - { - "description": "Authorization code from Keycloak", - "in": "query", - "name": "code", - "required": true, - "type": "string" - }, - { - "description": "State parameter for CSRF protection", - "in": "query", - "name": "state", - "required": true, - "type": "string" - }, - { - "description": "Frontend URL to redirect after successful authentication", - "in": "query", - "name": "redirect_url", - "type": "string" - }, - { - "description": "Error from Keycloak (if authentication failed)", - "in": "query", - "name": "error", - "type": "string" - }, - { - "description": "Error description from Keycloak", - "in": "query", - "name": "error_description", - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "302": { - "description": "Redirects to frontend URL with tokens in URL fragment" - }, - "400": { - "description": "Missing code or state, or Keycloak error", - "schema": { - "properties": { - "error": { - "type": "string" - } - }, - "type": "object" - } - }, - "401": { - "description": "Invalid state parameter", - "schema": { - "properties": { - "error": { - "type": "string" - } - }, - "type": "object" - } - }, - "500": { - "description": "Failed to exchange code for tokens", - "schema": { - "properties": { - "error": { - "type": "string" - } - }, - "type": "object" - } - } - }, - "summary": "Handle Keycloak OAuth2 callback", - "tags": [ - "Authentication API" - ] - } - }, - "/auth/keycloak/login": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Redirects the user to Keycloak's authorization endpoint to authenticate. Returns the authorization URL for frontend redirection with PKCE.", - "parameters": [ - { - "description": "URL to redirect after successful login", - "in": "query", - "name": "redirect_url", - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Authorization URL and state parameter", - "schema": { - "properties": { - "authorization_url": { - "type": "string" - }, - "state": { - "type": "string" - } - }, - "type": "object" - } - }, - "500": { - "description": "Failed to generate state or PKCE parameters", - "schema": { - "properties": { - "error": { - "type": "string" - } - }, - "type": "object" - } - } - }, - "summary": "Initiate Keycloak OAuth2 login", - "tags": [ - "Authentication API" - ] - } - }, - "/auth/login": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Returns the Keycloak authorization URL for frontend to redirect users. Supports OAuth2 authorization code flow with PKCE.", - "parameters": [ - { - "description": "URL to redirect after successful login", - "in": "query", - "name": "redirect_url", - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Authorization URL and state parameter", - "schema": { - "properties": { - "authorization_url": { - "type": "string" - }, - "state": { - "type": "string" - } - }, - "type": "object" - } - }, - "500": { - "description": "Failed to initiate login", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "summary": "Initiate Keycloak OAuth2 login", - "tags": [ - "Authentication API" - ] - } - }, - "/auth/logout": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Revokes the current access token and clears authentication cookies. After logout, the user must re-authenticate.", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully logged out", - "schema": { - "type": "object" - } - }, - "401": { - "description": "Unauthorized - invalid token", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Logout user", - "tags": [ - "Authentication API" - ] - } - }, - "/auth/me": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Returns the authenticated user's profile information including user ID, email, roles, and guest status.", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "User profile information", - "schema": { - "type": "object" - } - }, - "401": { - "description": "Unauthorized - invalid or expired token", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get current user information", - "tags": [ - "Authentication API" - ] - } - }, - "/auth/refresh-token": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Exchanges a valid refresh token for a new access token. Refresh token must be provided in Authorization header or refresh_token cookie.", - "parameters": [ - { - "description": "Refresh token (can also be in Authorization header)", - "in": "body", - "name": "refresh_token", - "schema": { - "type": "string" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "New access token and refresh token", - "schema": { - "type": "object" - } - }, - "401": { - "description": "Unauthorized - invalid or expired refresh token", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "summary": "Refresh access token", - "tags": [ - "Authentication API" - ] - } - }, - "/auth/revoke": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Revokes a refresh token to invalidate it", - "parameters": [ - { - "description": "Token to revoke", - "in": "body", - "name": "request", - "required": true, - "schema": { - "properties": { - "refresh_token": { - "type": "string" - } - }, - "type": "object" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Token revoked successfully", - "schema": { - "properties": { - "message": { - "type": "string" - } - }, - "type": "object" - } - }, - "400": { - "description": "Invalid request body", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Keycloak OAuth is not configured", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "summary": "Revoke Keycloak refresh token", - "tags": [ - "Authentication API" - ] - } - }, - "/auth/upgrade": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Converts a guest user account to a permanent account with email/password credentials. Guest flag is removed and user gains full access.", - "parameters": [ - { - "description": "Upgrade request with email and password", - "in": "body", - "name": "request", - "required": true, - "schema": { - "type": "object" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Account upgraded successfully with new tokens", - "schema": { - "type": "object" - } - }, - "400": { - "description": "Invalid request - missing email or password", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - not a guest user or invalid token", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Upgrade guest to permanent account", - "tags": [ - "Authentication API" - ] - } - }, - "/auth/validate": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Validates an access token against Keycloak's userinfo endpoint", - "parameters": [ - { - "description": "Bearer token", - "in": "header", - "name": "Authorization", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Token is valid with user information", - "schema": { - "properties": { - "user_info": { - "type": "object" - }, - "valid": { - "type": "boolean" - } - }, - "type": "object" - } - }, - "401": { - "description": "Invalid or expired token", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Keycloak OAuth is not configured", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "summary": "Validate Keycloak access token", - "tags": [ - "Authentication API" - ] - } - }, - "/auth/validate-api-key": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Internal endpoint used by Kong API Gateway to validate API keys. Not intended for direct client use.", - "parameters": [ - { - "description": "API key validation request", - "in": "body", - "name": "request", - "required": true, - "schema": { - "type": "object" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "API key is valid with user information", - "schema": { - "type": "object" - } - }, - "401": { - "description": "Invalid API key", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "summary": "Validate API key (Kong Plugin)", - "tags": [ - "Authentication API" - ] - } - }, - "/mcp": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Handles Model Context Protocol (MCP) requests over HTTP. Supports MCP methods: initialize, ping, tools/list, tools/call, prompts/list, prompts/call, resources/list, resources/read.\n\n**Available Tools:**\n- `google_search`: Web search via pluggable engines (Serper/SearXNG) with params: q, gl, hl, location, num, tbs, page, autocorrect, domain_allow_list, location_hint, offline_mode. Returns structured citations.\n- `scrape`: Web page scraping (params: url, includeMarkdown) returning text, preview, cache_status, and metadata.\n- `file_search_index` / `file_search_query`: Index arbitrary text and run similarity queries against the lightweight vector store.\n- `python_exec`: Execute trusted code through SandboxFusion (params: code, language, session_id, approved) to retrieve stdout/stderr/artifacts.\n- `memory_retrieve`: Retrieve relevant user preferences, project context, or conversation history (params: query, user_id, project_id, max_user_items, max_project_items, min_similarity). Returns personalized context.\n- `generate_image`: Generate images from a text prompt via LLM API /v1/images/generations (params: prompt, size, n, num_inference_steps, cfg_scale).\n- `edit_image`: Edit images with a prompt + input image via LLM API /v1/images/edits (params: prompt, image, mask, size, strength, steps, seed, cfg_scale).\n\n**MCP Protocol:**\n- Request format: JSON-RPC 2.0 with method and params\n- Response format: Server-Sent Events (SSE) stream\n- Stateless mode (no session management)", - "parameters": [ - { - "description": "MCP JSON-RPC request payload (e.g., {\\", - "in": "body", - "name": "request", - "required": true, - "schema": { - "type": "object" - } - } - ], - "produces": [ - "text/event-stream" - ], - "responses": { - "200": { - "description": "Streamed MCP response in SSE format", - "schema": { - "type": "string" - } - }, - "400": { - "description": "Invalid MCP request payload or unsupported method", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "summary": "MCP endpoint for tool execution", - "tags": [ - "MCP API" - ] - } - }, - "/v1/admin/mcp-tools": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Get a paginated list of MCP tools with optional filtering", - "parameters": [ - { - "description": "Filter by category", - "in": "query", - "name": "category", - "type": "string" - }, - { - "description": "Filter by active status", - "in": "query", - "name": "is_active", - "type": "boolean" - }, - { - "description": "Search in name, description, and tool_key", - "in": "query", - "name": "search", - "type": "string" - }, - { - "default": 20, - "description": "Limit", - "in": "query", - "name": "limit", - "type": "integer" - }, - { - "default": 0, - "description": "Offset", - "in": "query", - "name": "offset", - "type": "integer" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/mcptoolhandler.ListResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "List MCP tools", - "tags": [ - "Admin - MCP Tools" - ] - } - }, - "/v1/admin/mcp-tools/{id}": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Get an MCP tool by public ID", - "parameters": [ - { - "description": "Public ID", - "in": "path", - "name": "id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/mcptoolhandler.MCPToolResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Get an MCP tool", - "tags": [ - "Admin - MCP Tools" - ] - }, - "patch": { - "consumes": [ - "application/json" - ], - "description": "Update an existing MCP tool configuration (Name is read-only)", - "parameters": [ - { - "description": "Public ID", - "in": "path", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Request body", - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/mcptool.UpdateMCPToolRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/mcptoolhandler.MCPToolResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Update an MCP tool", - "tags": [ - "Admin - MCP Tools" - ] - } - }, - "/v1/admin/models/catalogs": { - "get": { - "description": "Retrieves a paginated list of model catalogs with optional filtering and searching", - "parameters": [ - { - "description": "Number of records to return (default: 20, max: 100)", - "in": "query", - "name": "limit", - "type": "integer" - }, - { - "description": "Number of records to skip for pagination", - "in": "query", - "name": "offset", - "type": "integer" - }, - { - "description": "Sort order: asc or desc (default: desc)", - "in": "query", - "name": "order", - "type": "string" - }, - { - "description": "Filter by status: init, filled, updated", - "in": "query", - "name": "status", - "type": "string" - }, - { - "description": "Filter by moderation status", - "in": "query", - "name": "is_moderated", - "type": "boolean" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "List of model catalogs", - "schema": { - "$ref": "#/definitions/modelresponses.ModelCatalogResponse" - } - }, - "400": { - "description": "Invalid query parameters", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "List all model catalogs", - "tags": [ - "Admin Model API" - ] - } - }, - "/v1/admin/models/catalogs/bulk-toggle": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Enable or disable provider models for specific catalogs or ALL catalogs, with optional exception list. Supports \"enable/disable all except\" patterns globally or scoped to catalogs.", - "parameters": [ - { - "description": "Bulk toggle request. If catalog_ids is empty, applies to ALL catalogs. Use except_models to exclude specific models.", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/requestmodels.BulkToggleCatalogsRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Bulk operation result with counts and status", - "schema": { - "$ref": "#/definitions/modelresponses.BulkOperationResponse" - } - }, - "400": { - "description": "Invalid request - exceeds limits or validation error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "One or more catalog IDs not found (when catalog_ids provided)", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error during bulk operation", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Bulk enable/disable provider models by catalog IDs or all catalogs", - "tags": [ - "Admin Model API" - ] - } - }, - "/v1/admin/models/catalogs/{model_public_id}": { - "get": { - "description": "Retrieves detailed information about a model catalog entry by its public ID (supports IDs with slashes)", - "parameters": [ - { - "description": "Model Catalog Public ID (can contain slashes)", - "in": "path", - "name": "model_public_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Model catalog details", - "schema": { - "$ref": "#/definitions/modelresponses.ModelCatalogResponse" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Model catalog not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get a model catalog entry", - "tags": [ - "Admin Model API" - ] - }, - "patch": { - "consumes": [ - "application/json" - ], - "description": "Updates metadata for a model catalog entry. Marks it as manually updated to prevent auto-sync overwrites.", - "parameters": [ - { - "description": "Model Catalog Public ID (can contain slashes)", - "in": "path", - "name": "model_public_id", - "required": true, - "type": "string" - }, - { - "description": "Update payload", - "in": "body", - "name": "payload", - "required": true, - "schema": { - "$ref": "#/definitions/requestmodels.UpdateModelCatalogRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Updated model catalog", - "schema": { - "$ref": "#/definitions/modelresponses.ModelCatalogResponse" - } - }, - "400": { - "description": "Invalid request payload", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Model catalog not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Update a model catalog entry", - "tags": [ - "Admin Model API" - ] - } - }, - "/v1/admin/models/prompt-templates/assign/{model_id}": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Assign or update a prompt template assignment for a model catalog", - "parameters": [ - { - "description": "Model Catalog Public ID", - "in": "path", - "name": "model_id", - "required": true, - "type": "string" - }, - { - "description": "Assignment request", - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/modelprompthandler.AssignRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/modelprompthandler.ModelPromptTemplateResponse" - } - }, - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/modelprompthandler.ModelPromptTemplateResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Assign a prompt template to a model", - "tags": [ - "Admin - Model Prompt Templates" - ] - } - }, - "/v1/admin/models/prompt-templates/effective/{model_id}": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Get all resolved templates for a model, including global defaults", - "parameters": [ - { - "description": "Model Catalog Public ID", - "in": "path", - "name": "model_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/modelprompthandler.EffectiveTemplatesResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Get effective templates for a model", - "tags": [ - "Admin - Model Prompt Templates" - ] - } - }, - "/v1/admin/models/prompt-templates/list/{model_id}": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Get all prompt template assignments for a model catalog", - "parameters": [ - { - "description": "Model Catalog Public ID", - "in": "path", - "name": "model_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/modelprompthandler.ListResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "List model prompt template assignments", - "tags": [ - "Admin - Model Prompt Templates" - ] - } - }, - "/v1/admin/models/prompt-templates/unassign/{template_key}/{model_id}": { - "delete": { - "consumes": [ - "application/json" - ], - "description": "Remove a prompt template assignment, reverting to global default", - "parameters": [ - { - "description": "Model Catalog Public ID", - "in": "path", - "name": "model_id", - "required": true, - "type": "string" - }, - { - "description": "Template Key (e.g., deep_research, timing)", - "in": "path", - "name": "template_key", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "204": { - "description": "No Content" - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Remove a prompt template assignment from a model", - "tags": [ - "Admin - Model Prompt Templates" - ] - } - }, - "/v1/admin/models/prompt-templates/update/{template_key}/{model_id}": { - "patch": { - "consumes": [ - "application/json" - ], - "description": "Update an existing prompt template assignment", - "parameters": [ - { - "description": "Model Catalog Public ID", - "in": "path", - "name": "model_id", - "required": true, - "type": "string" - }, - { - "description": "Template Key", - "in": "path", - "name": "template_key", - "required": true, - "type": "string" - }, - { - "description": "Update request", - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/modelprompthandler.UpdateRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/modelprompthandler.ModelPromptTemplateResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Update a prompt template assignment", - "tags": [ - "Admin - Model Prompt Templates" - ] - } - }, - "/v1/admin/models/provider-models": { - "get": { - "description": "Retrieves a paginated list of provider models with optional filtering", - "parameters": [ - { - "description": "Number of records to return (default: 20, max: 100)", - "in": "query", - "name": "limit", - "type": "integer" - }, - { - "description": "Number of records to skip for pagination", - "in": "query", - "name": "offset", - "type": "integer" - }, - { - "description": "Sort order: asc or desc (default: desc)", - "in": "query", - "name": "order", - "type": "string" - }, - { - "description": "Filter by provider public ID", - "in": "query", - "name": "provider_id", - "type": "string" - }, - { - "description": "Filter by model key", - "in": "query", - "name": "model_key", - "type": "string" - }, - { - "description": "Filter by active status", - "in": "query", - "name": "active", - "type": "boolean" - }, - { - "description": "Filter by image support", - "in": "query", - "name": "supports_images", - "type": "boolean" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "List of provider models", - "schema": { - "$ref": "#/definitions/modelresponses.ProviderModelResponse" - } - }, - "400": { - "description": "Invalid query parameters", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "List all provider models", - "tags": [ - "Admin Model API" - ] - } - }, - "/v1/admin/models/provider-models/bulk-toggle": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Enables or disables provider models with flexible patterns: enable all, disable all, enable all except, or disable all except. Optionally filter by provider.", - "parameters": [ - { - "description": "Bulk toggle payload with enable flag, optional provider filter, and exception list", - "in": "body", - "name": "payload", - "required": true, - "schema": { - "$ref": "#/definitions/requestmodels.BulkEnableModelsRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Bulk operation result with counts and status", - "schema": { - "$ref": "#/definitions/modelresponses.BulkOperationResponse" - } - }, - "400": { - "description": "Invalid request payload", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Bulk enable or disable provider models", - "tags": [ - "Admin Model API" - ] - } - }, - "/v1/admin/models/provider-models/{provider_model_public_id}": { - "get": { - "description": "Retrieves detailed information about a provider model by its public ID", - "parameters": [ - { - "description": "Provider Model Public ID", - "in": "path", - "name": "provider_model_public_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Provider model details", - "schema": { - "$ref": "#/definitions/modelresponses.ProviderModelResponse" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Provider model not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get a provider model", - "tags": [ - "Admin Model API" - ] - }, - "patch": { - "consumes": [ - "application/json" - ], - "description": "Updates configuration for a provider model including pricing, limits, and feature flags", - "parameters": [ - { - "description": "Provider Model Public ID", - "in": "path", - "name": "provider_model_public_id", - "required": true, - "type": "string" - }, - { - "description": "Update payload", - "in": "body", - "name": "payload", - "required": true, - "schema": { - "$ref": "#/definitions/requestmodels.UpdateProviderModelRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Updated provider model", - "schema": { - "$ref": "#/definitions/modelresponses.ProviderModelResponse" - } - }, - "400": { - "description": "Invalid request payload", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Provider model not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Update a provider model", - "tags": [ - "Admin Model API" - ] - } - }, - "/v1/admin/prompt-templates": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Get a paginated list of prompt templates with optional filtering", - "parameters": [ - { - "description": "Filter by category", - "in": "query", - "name": "category", - "type": "string" - }, - { - "description": "Filter by active status", - "in": "query", - "name": "is_active", - "type": "boolean" - }, - { - "description": "Filter by system status", - "in": "query", - "name": "is_system", - "type": "boolean" - }, - { - "description": "Search in name and description", - "in": "query", - "name": "search", - "type": "string" - }, - { - "default": 20, - "description": "Limit", - "in": "query", - "name": "limit", - "type": "integer" - }, - { - "default": 0, - "description": "Offset", - "in": "query", - "name": "offset", - "type": "integer" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/prompttemplatehandler.ListResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "List prompt templates", - "tags": [ - "Admin - Prompt Templates" - ] - }, - "post": { - "consumes": [ - "application/json" - ], - "description": "Create a new prompt template", - "parameters": [ - { - "description": "Request body", - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/prompttemplate.CreatePromptTemplateRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/prompttemplatehandler.PromptTemplateResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "409": { - "description": "Conflict", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Create a prompt template", - "tags": [ - "Admin - Prompt Templates" - ] - } - }, - "/v1/admin/prompt-templates/{id}": { - "delete": { - "consumes": [ - "application/json" - ], - "description": "Delete a prompt template (system templates cannot be deleted)", - "parameters": [ - { - "description": "Public ID", - "in": "path", - "name": "id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "204": { - "description": "No Content" - }, - "403": { - "description": "Forbidden", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Delete a prompt template", - "tags": [ - "Admin - Prompt Templates" - ] - }, - "get": { - "consumes": [ - "application/json" - ], - "description": "Get a prompt template by public ID", - "parameters": [ - { - "description": "Public ID", - "in": "path", - "name": "id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/prompttemplatehandler.PromptTemplateResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Get a prompt template", - "tags": [ - "Admin - Prompt Templates" - ] - }, - "patch": { - "consumes": [ - "application/json" - ], - "description": "Update an existing prompt template", - "parameters": [ - { - "description": "Public ID", - "in": "path", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Request body", - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/prompttemplate.UpdatePromptTemplateRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/prompttemplatehandler.PromptTemplateResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Update a prompt template", - "tags": [ - "Admin - Prompt Templates" - ] - } - }, - "/v1/admin/prompt-templates/{id}/duplicate": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Create a copy of an existing prompt template with a new name (key is auto-generated)", - "parameters": [ - { - "description": "Public ID", - "in": "path", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Request body", - "in": "body", - "name": "body", - "required": true, - "schema": { - "$ref": "#/definitions/prompttemplatehandler.DuplicateRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/prompttemplatehandler.PromptTemplateResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "409": { - "description": "Conflict", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Duplicate a prompt template", - "tags": [ - "Admin - Prompt Templates" - ] - } - }, - "/v1/admin/providers": { - "get": { - "description": "Retrieves all providers with their model counts", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "List of providers with model counts", - "schema": { - "items": { - "$ref": "#/definitions/modelresponses.ProviderWithModelCountResponse" - }, - "type": "array" - } - }, - "500": { - "description": "Failed to retrieve providers", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get all providers", - "tags": [ - "Admin Provider API" - ] - }, - "post": { - "consumes": [ - "application/json" - ], - "description": "Registers a new provider and synchronizes its available models.", - "parameters": [ - { - "description": "Provider registration payload", - "in": "body", - "name": "payload", - "required": true, - "schema": { - "$ref": "#/definitions/requestmodels.AddProviderRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Registered provider with synced models", - "schema": { - "$ref": "#/definitions/modelresponses.ProviderWithModelsResponse" - } - }, - "400": { - "description": "Invalid request payload", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Failed to register provider", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Register a provider", - "tags": [ - "Admin Provider API" - ] - } - }, - "/v1/admin/providers/{provider_public_id}": { - "delete": { - "description": "Deletes a provider by its public ID along with its provider models", - "parameters": [ - { - "description": "Provider public ID", - "in": "path", - "name": "provider_public_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "204": { - "description": "Provider deleted" - }, - "404": { - "description": "Provider not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Failed to delete provider", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Delete a provider", - "tags": [ - "Admin Provider API" - ] - }, - "get": { - "description": "Retrieves a provider by its public ID", - "parameters": [ - { - "description": "Provider public ID", - "in": "path", - "name": "provider_public_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Provider details", - "schema": { - "$ref": "#/definitions/modelresponses.ProviderResponse" - } - }, - "404": { - "description": "Provider not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Failed to retrieve provider", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get a provider", - "tags": [ - "Admin Provider API" - ] - }, - "patch": { - "consumes": [ - "application/json" - ], - "description": "Updates an existing provider's configuration", - "parameters": [ - { - "description": "Provider public ID", - "in": "path", - "name": "provider_public_id", - "required": true, - "type": "string" - }, - { - "description": "Provider update payload", - "in": "body", - "name": "payload", - "required": true, - "schema": { - "$ref": "#/definitions/requestmodels.UpdateProviderRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Updated provider", - "schema": { - "$ref": "#/definitions/modelresponses.ProviderResponse" - } - }, - "400": { - "description": "Invalid request payload", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Provider not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Failed to update provider", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Update a provider", - "tags": [ - "Admin Provider API" - ] - } - }, - "/v1/admin/usage": { - "get": { - "description": "Returns total platform token usage including top users and breakdown by model/provider", - "parameters": [ - { - "description": "Start date (YYYY-MM-DD), defaults to 30 days ago", - "in": "query", - "name": "start_date", - "type": "string" - }, - { - "description": "End date (YYYY-MM-DD), defaults to today", - "in": "query", - "name": "end_date", - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/tokenusage.PlatformUsageResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get platform-wide token usage (Admin only)", - "tags": [ - "Usage" - ] - } - }, - "/v1/auth/logout": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Remove refresh tokens to perform logout and invalidate Keycloak session. Accepts refresh token from cookie, Authorization header, or request body.", - "parameters": [ - { - "description": "Refresh token to revoke", - "in": "body", - "name": "refresh_token", - "schema": { - "type": "string" - } - }, - { - "description": "Bearer refresh_token", - "in": "header", - "name": "Authorization", - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully logged out", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "summary": "Logout", - "tags": [ - "Authentication API" - ] - }, - "post": { - "consumes": [ - "application/json" - ], - "description": "Remove refresh tokens to perform logout and invalidate Keycloak session. Accepts refresh token from cookie, Authorization header, or request body.", - "parameters": [ - { - "description": "Refresh token to revoke", - "in": "body", - "name": "refresh_token", - "schema": { - "type": "string" - } - }, - { - "description": "Bearer refresh_token", - "in": "header", - "name": "Authorization", - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully logged out", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "summary": "Logout", - "tags": [ - "Authentication API" - ] - } - }, - "/v1/auth/me": { - "get": { - "description": "Retrieves the profile of the authenticated user", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully retrieved user profile", - "schema": { - "$ref": "#/definitions/authhandler.GetMeResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get user profile", - "tags": [ - "Authentication API" - ] - } - }, - "/v1/auth/refresh-token": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Use a valid refresh token to obtain a new access token", - "parameters": [ - { - "description": "Refresh token (can also be in Authorization header)", - "in": "body", - "name": "refresh_token", - "schema": { - "type": "string" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully refreshed the access token", - "schema": { - "$ref": "#/definitions/authhandler.AccessTokenResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "summary": "Refresh an access token", - "tags": [ - "Authentication API" - ] - } - }, - "/v1/chat/completions": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Generates a model response for the given chat conversation. This is a standard chat completion API that supports both streaming and non-streaming modes without conversation persistence.\n\n**Streaming Mode (stream=true):**\n- Returns Server-Sent Events (SSE) with real-time streaming\n- Streams completion chunks directly from the inference model\n- Final event contains \"[DONE]\" marker\n\n**Non-Streaming Mode (stream=false or omitted):**\n- Returns single JSON response with complete completion\n- Standard OpenAI ChatCompletionResponse format\n\n**Storage Options:**\n- `store=true`: Persist the latest input message and assistant response to the active conversation\n- `store_reasoning=true`: Additionally persist reasoning content provided by the model\n- When `store` is omitted or false, the conversation remains read-only\n\n**Features:**\n- Supports all OpenAI ChatCompletionRequest parameters\n- Optional conversation context for conversation persistence\n- User authentication required\n- Direct inference model integration", - "parameters": [ - { - "description": "Chat completion request with streaming options and optional conversation", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/chatrequests.ChatCompletionRequest" - } - } - ], - "produces": [ - "application/json", - "text/event-stream" - ], - "responses": { - "200": { - "description": "Successful streaming response (when stream=true) - SSE format with data: {json} events", - "schema": { - "type": "string" - } - }, - "400": { - "description": "Invalid request payload, empty messages, or inference failure", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Create a chat completion", - "tags": [ - "Chat Completions API" - ] - } - }, - "/v1/conversations": { - "delete": { - "description": "Permanently delete all conversations for the authenticated user\n\n**WARNING: This is a destructive operation that cannot be undone.**\n\n**Features:**\n- Deletes ALL conversations owned by the authenticated user\n- Automatically cascades to delete all associated items and shares\n- Returns the count of deleted conversations\n- Requires valid authentication to ensure ownership verification\n\n**Security:**\n- Only deletes conversations owned by the authenticated user\n- Cannot delete other users' conversations\n- Authentication is mandatory", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully deleted all conversations", - "schema": { - "$ref": "#/definitions/conversationresponses.BulkConversationsDeletedResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error - deletion failed", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Delete all conversations", - "tags": [ - "Conversations API" - ] - }, - "get": { - "description": "List conversations for the authenticated user with optional referrer filtering.", - "parameters": [ - { - "description": "Referrer filter", - "in": "query", - "name": "referrer", - "type": "string" - }, - { - "description": "Maximum number of conversations to return", - "in": "query", - "name": "limit", - "type": "integer" - }, - { - "description": "Return conversations created after the given numeric ID", - "in": "query", - "name": "after", - "type": "string" - }, - { - "description": "Sort order (asc or desc)", - "in": "query", - "name": "order", - "type": "string" - }, - { - "description": "Set to 'all' to list conversations across the workspace (requires elevated permissions)", - "in": "query", - "name": "scope", - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully retrieved conversations", - "schema": { - "$ref": "#/definitions/conversationresponses.ConversationListResponse" - } - }, - "400": { - "description": "Invalid request parameters", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "List conversations", - "tags": [ - "Conversations API" - ] - }, - "post": { - "consumes": [ - "application/json" - ], - "description": "Create a new conversation to store and retrieve conversation state across Response API calls\n\n**Features:**\n- Create conversation with optional metadata (max 16 key-value pairs)\n- Add up to 20 initial items to the conversation\n- Returns conversation ID with `conv_` prefix\n- Supports OpenAI Conversations API format\n\n**Metadata Constraints:**\n- Maximum 16 key-value pairs\n- Keys: max 64 characters\n- Values: max 512 characters", - "parameters": [ - { - "description": "Create conversation request with optional items and metadata", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/conversationrequests.CreateConversationRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully created conversation", - "schema": { - "$ref": "#/definitions/conversationresponses.ConversationResponse" - } - }, - "400": { - "description": "Invalid request - validation failed or too many items", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error - conversation creation failed", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Create a conversation", - "tags": [ - "Conversations API" - ] - } - }, - "/v1/conversations/{conv_public_id}": { - "delete": { - "description": "Delete a conversation (soft delete). Items in the conversation will not be deleted but will be inaccessible.\n\n**Features:**\n- Soft delete (conversation marked as deleted, not physically removed)\n- Items remain in database but become inaccessible\n- Automatic ownership verification\n- Returns deletion confirmation with conversation ID\n\n**Response:**\n- `id`: Deleted conversation ID\n- `object`: Always \"conversation.deleted\"\n- `deleted`: Always true", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully deleted conversation", - "schema": { - "$ref": "#/definitions/conversationresponses.ConversationDeletedResponse" - } - }, - "400": { - "description": "Invalid conversation ID format", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Conversation not found or access denied", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error - deletion failed", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Delete a conversation", - "tags": [ - "Conversations API" - ] - }, - "get": { - "description": "Retrieve a conversation by ID with ownership verification\n\n**Features:**\n- Retrieves conversation metadata including creation timestamp\n- Automatic ownership verification (user can only access their own conversations)\n- Returns OpenAI-compatible conversation object\n\n**Response Fields:**\n- `id`: Conversation ID with `conv_` prefix\n- `object`: Always \"conversation\"\n- `created_at`: Unix timestamp\n- `metadata`: User-defined key-value pairs", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully retrieved conversation", - "schema": { - "$ref": "#/definitions/conversationresponses.ConversationResponse" - } - }, - "400": { - "description": "Invalid conversation ID format", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Conversation not found or access denied", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get a conversation", - "tags": [ - "Conversations API" - ] - }, - "post": { - "consumes": [ - "application/json" - ], - "description": "Update a conversation's metadata while preserving existing items\n\n**Features:**\n- Update metadata key-value pairs\n- Replaces entire metadata object (not merged)\n- Items remain unchanged\n- Automatic ownership verification\n\n**Metadata Constraints:**\n- Maximum 16 key-value pairs\n- Keys: max 64 characters\n- Values: max 512 characters", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "description": "Update conversation request with new metadata", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/conversationrequests.UpdateConversationRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully updated conversation", - "schema": { - "$ref": "#/definitions/conversationresponses.ConversationResponse" - } - }, - "400": { - "description": "Invalid request - validation failed or invalid metadata", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Conversation not found or access denied", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error - update failed", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Update a conversation", - "tags": [ - "Conversations API" - ] - } - }, - "/v1/conversations/{conv_public_id}/branches": { - "get": { - "description": "List all branches for a conversation", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully retrieved branches", - "schema": { - "$ref": "#/definitions/conversationhandler.ListBranchesResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Conversation not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "List branches", - "tags": [ - "Conversation Branches" - ] - }, - "post": { - "consumes": [ - "application/json" - ], - "description": "Create a new branch in a conversation, optionally forking from an existing item", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "description": "Create branch request", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/conversationhandler.CreateBranchRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "201": { - "description": "Successfully created branch", - "schema": { - "$ref": "#/definitions/conversationhandler.BranchResponse" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Conversation not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Create a branch", - "tags": [ - "Conversation Branches" - ] - } - }, - "/v1/conversations/{conv_public_id}/branches/{branch_name}": { - "delete": { - "description": "Delete a branch from a conversation (cannot delete MAIN or active branch)", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "description": "Branch name", - "in": "path", - "name": "branch_name", - "required": true, - "type": "string" - } - ], - "responses": { - "204": { - "description": "Branch deleted successfully" - }, - "400": { - "description": "Cannot delete MAIN or active branch", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Branch not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Delete a branch", - "tags": [ - "Conversation Branches" - ] - }, - "get": { - "description": "Get details of a specific branch", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "description": "Branch name", - "in": "path", - "name": "branch_name", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully retrieved branch", - "schema": { - "$ref": "#/definitions/conversationhandler.BranchResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Branch not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get branch details", - "tags": [ - "Conversation Branches" - ] - } - }, - "/v1/conversations/{conv_public_id}/branches/{branch_name}/activate": { - "post": { - "description": "Set a branch as the active branch for a conversation", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "description": "Branch name", - "in": "path", - "name": "branch_name", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Branch activated successfully", - "schema": { - "$ref": "#/definitions/conversationhandler.ActivateBranchResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Branch not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Activate a branch", - "tags": [ - "Conversation Branches" - ] - } - }, - "/v1/conversations/{conv_public_id}/items": { - "get": { - "description": "List all items in a conversation with cursor-based pagination support\n\n**Features:**\n- Cursor-based pagination using item IDs\n- Configurable page size (1-100 items, default 20)\n- Sort order control (ascending or descending)\n- Optional include parameter for additional fields\n- Returns paginated list with navigation cursors\n\n**Pagination:**\n- Use `after` cursor from previous response for next page\n- `has_more` indicates if more items are available\n- `first_id` and `last_id` provide cursor references\n\n**Query Parameters:**\n- `limit`: Number of items (1-100, default 20)\n- `order`: Sort order (\"asc\" or \"desc\", default \"desc\")\n- `after`: Item ID cursor for pagination\n- `include`: Additional fields to include (optional)", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "description": "Item ID cursor to list items after (pagination)", - "in": "query", - "name": "after", - "type": "string" - }, - { - "default": 20, - "description": "Number of items to return (1-100)", - "in": "query", - "maximum": 100, - "minimum": 1, - "name": "limit", - "type": "integer" - }, - { - "default": "desc", - "description": "Sort order: asc or desc", - "enum": [ - "asc", - "desc" - ], - "in": "query", - "name": "order", - "type": "string" - }, - { - "collectionFormat": "csv", - "description": "Additional fields to include in response", - "in": "query", - "items": { - "type": "string" - }, - "name": "include", - "type": "array" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully retrieved items list", - "schema": { - "$ref": "#/definitions/conversationresponses.ItemListResponse" - } - }, - "400": { - "description": "Invalid request - invalid parameters or conversation ID", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Conversation not found or access denied", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error - listing failed", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "List conversation items", - "tags": [ - "Conversations API" - ] - }, - "post": { - "consumes": [ - "application/json" - ], - "description": "Add items to a conversation. You may add up to 20 items at a time.\n\n**Features:**\n- Bulk item creation (max 20 items per request)\n- Automatic item ID generation with `msg_` prefix\n- Items added to conversation's active branch (default: MAIN)\n- Returns list of created items with generated IDs\n\n**Item Types:**\n- `message`: User or assistant messages\n- `tool_call`: Tool/function call items\n- `tool_response`: Tool/function response items\n- Other OpenAI-compatible item types\n\n**Constraints:**\n- Maximum 20 items per request\n- Each item must have valid type and content\n- Items are immutable after creation", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "collectionFormat": "csv", - "description": "Additional fields to include in response", - "in": "query", - "items": { - "type": "string" - }, - "name": "include", - "type": "array" - }, - { - "description": "Create items request with array of items", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/conversationrequests.CreateItemsRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully created items", - "schema": { - "$ref": "#/definitions/conversationresponses.ConversationItemCreatedResponse" - } - }, - "400": { - "description": "Invalid request - too many items, invalid format, or validation failed", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Conversation not found or access denied", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error - item creation failed", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Create conversation items", - "tags": [ - "Conversations API" - ] - } - }, - "/v1/conversations/{conv_public_id}/items/by-call-id/{call_id}": { - "patch": { - "consumes": [ - "application/json" - ], - "description": "Update a conversation item's status and output using its call_id.\nThis endpoint is primarily used by MCP tools to report tool execution results.\n\n**Features:**\n- Find item by call_id (e.g., call_xxx) instead of item_id\n- Update status to completed, failed, or cancelled\n- Store tool output or error message\n- Automatic timestamp for completion\n\n**Use Cases:**\n- MCP tool reports successful execution with output\n- MCP tool reports failure with error message\n- Tool call status tracking and observability", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "description": "Call ID of the tool call item (format: call_xxxxx)", - "in": "path", - "name": "call_id", - "required": true, - "type": "string" - }, - { - "description": "Update request with status and optional output/error", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/conversationrequests.UpdateItemByCallIDRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully updated item", - "schema": { - "$ref": "#/definitions/conversationresponses.ItemResponse" - } - }, - "400": { - "description": "Invalid request - validation failed", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Conversation or item not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Update item by call ID", - "tags": [ - "Conversations API" - ] - } - }, - "/v1/conversations/{conv_public_id}/items/{item_id}": { - "delete": { - "description": "Delete an item from a conversation by creating a new MAIN branch without it.\nThe old MAIN branch is preserved as a backup.\n\n**Features:**\n- Creates a new branch without the deleted item\n- New branch becomes MAIN, old MAIN becomes backup\n- Automatic ownership verification\n- Preserves conversation history in backup branch\n\n**Important:**\n- The old MAIN branch is renamed to MAIN_YYYYMMDDHHMMSS\n- You can switch back to the backup branch if needed\n- This is a non-destructive delete operation\n\n**Response:**\nReturns branch information including the backup branch name", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "description": "Item ID to delete (format: msg_xxxxx)", - "in": "path", - "name": "item_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully deleted item, returns branch info", - "schema": { - "$ref": "#/definitions/conversationhandler.DeleteItemResponse" - } - }, - "400": { - "description": "Invalid conversation ID or item ID format", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Conversation or item not found, or access denied", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error - deletion failed", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Delete a conversation item", - "tags": [ - "Conversations API" - ] - }, - "get": { - "description": "Retrieve a single item from a conversation by item ID\n\n**Features:**\n- Retrieve specific item by ID\n- Returns complete item with all content\n- Automatic ownership verification via conversation\n- Optional include parameter for additional fields\n\n**Response Fields:**\n- `id`: Item ID with `msg_` prefix\n- `type`: Item type (message, tool_call, etc.)\n- `role`: Role for message items (user, assistant)\n- `content`: Item content array\n- `status`: Item status (completed, incomplete, etc.)\n- `created_at`: Unix timestamp", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "description": "Item ID (format: msg_xxxxx)", - "in": "path", - "name": "item_id", - "required": true, - "type": "string" - }, - { - "collectionFormat": "csv", - "description": "Additional fields to include in response", - "in": "query", - "items": { - "type": "string" - }, - "name": "include", - "type": "array" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successfully retrieved item", - "schema": { - "$ref": "#/definitions/conversationresponses.ItemResponse" - } - }, - "400": { - "description": "Invalid conversation ID or item ID format", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Conversation or item not found, or access denied", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get a conversation item", - "tags": [ - "Conversations API" - ] - } - }, - "/v1/conversations/{conv_public_id}/items/{item_id}/edit": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Edit a user message and create a new branch with the edited content", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "description": "Message item ID (format: msg_xxxxx)", - "in": "path", - "name": "item_id", - "required": true, - "type": "string" - }, - { - "description": "Edit message request", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/conversationhandler.EditMessageRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Message edited successfully", - "schema": { - "$ref": "#/definitions/conversationhandler.EditMessageResponse" - } - }, - "400": { - "description": "Invalid request or not a user message", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Message not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Edit a message", - "tags": [ - "Message Actions" - ] - } - }, - "/v1/conversations/{conv_public_id}/items/{item_id}/regenerate": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Regenerate an assistant response by creating a new branch", - "parameters": [ - { - "description": "Conversation ID (format: conv_xxxxx)", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "description": "Assistant message item ID (format: msg_xxxxx)", - "in": "path", - "name": "item_id", - "required": true, - "type": "string" - }, - { - "description": "Regenerate options", - "in": "body", - "name": "request", - "schema": { - "$ref": "#/definitions/conversationhandler.RegenerateMessageRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Regeneration initiated", - "schema": { - "$ref": "#/definitions/conversationhandler.RegenerateMessageResponse" - } - }, - "400": { - "description": "Invalid request or not an assistant message", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Message not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Regenerate a response", - "tags": [ - "Message Actions" - ] - } - }, - "/v1/conversations/{conv_public_id}/share": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Creates a public share link for a conversation or a single message", - "parameters": [ - { - "description": "Conversation public ID", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "description": "Share creation request", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/sharerequests.CreateShareRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "201": { - "description": "Share created successfully", - "schema": { - "$ref": "#/definitions/shareresponses.ShareResponse" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Conversation not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "413": { - "description": "Snapshot too large", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Create a share for a conversation", - "tags": [ - "Shares API" - ] - } - }, - "/v1/conversations/{conv_public_id}/shares": { - "get": { - "description": "Lists all shares (active and revoked) for a conversation", - "parameters": [ - { - "description": "Conversation public ID", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "List of shares", - "schema": { - "$ref": "#/definitions/shareresponses.ShareListResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Conversation not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "List shares for a conversation", - "tags": [ - "Shares API" - ] - } - }, - "/v1/conversations/{conv_public_id}/shares/{share_id}": { - "delete": { - "description": "Revokes an active share, making it inaccessible", - "parameters": [ - { - "description": "Conversation public ID", - "in": "path", - "name": "conv_public_id", - "required": true, - "type": "string" - }, - { - "description": "Share public ID", - "in": "path", - "name": "share_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Share revoked successfully", - "schema": { - "$ref": "#/definitions/shareresponses.ShareDeletedResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Share not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Revoke a share", - "tags": [ - "Shares API" - ] - } - }, - "/v1/healthz": { - "get": { - "description": "Returns the health status of the API server. Used by orchestrators and monitoring systems.", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Health status OK", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Health check endpoint", - "tags": [ - "Server API" - ] - } - }, - "/v1/images/edits": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Creates an edited image given an original image and a prompt.", - "parameters": [ - { - "description": "Image edit request", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/image.ImageEditRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successful image edit response", - "schema": { - "$ref": "#/definitions/image.ImageGenerationResponse" - } - }, - "400": { - "description": "Invalid request payload or validation error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "No active image provider configured", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error or image provider error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Create image edit", - "tags": [ - "Images API" - ] - } - }, - "/v1/images/generations": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Generates images from a text prompt using the configured image provider.\nThis endpoint is compatible with the OpenAI Images API format.\n\n**Response Formats:**\n- url: Returns presigned URLs to download images (default, recommended)\n- b64_json: Returns base64-encoded image data\n\n**Size Options:**\n- 1024x1024 (default)\n- 512x512\n- 1792x1024 (landscape)\n- 1024x1792 (portrait)\n", - "parameters": [ - { - "description": "Image generation request", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/image.ImageGenerationRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Successful image generation response", - "schema": { - "$ref": "#/definitions/image.ImageGenerationResponse" - } - }, - "400": { - "description": "Invalid request payload or validation error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized - missing or invalid authentication", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "No active image provider configured", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error or image provider error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "501": { - "description": "Feature not implemented", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Create image generation", - "tags": [ - "Images API" - ] - } - }, - "/v1/images/variations": { - "post": { - "consumes": [ - "application/json" - ], - "description": "Creates a variation of a given image.\nNOTE: This endpoint is not yet implemented and will return 501 Not Implemented.", - "produces": [ - "application/json" - ], - "responses": { - "501": { - "description": "Not implemented", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Create image variation (Not Implemented)", - "tags": [ - "Images API" - ] - } - }, - "/v1/mcp-tools": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Get all active MCP tools (public endpoint for mcp-tools service)", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/mcptoolhandler.ActiveToolsResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "List all active MCP tools", - "tags": [ - "MCP Tools" - ] - } - }, - "/v1/mcp-tools/{key}": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Get an MCP tool by its unique tool key (public endpoint for mcp-tools service)", - "parameters": [ - { - "description": "Tool Key", - "in": "path", - "name": "key", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/mcptoolhandler.MCPToolResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Get an MCP tool by key", - "tags": [ - "MCP Tools" - ] - } - }, - "/v1/models": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Retrieves a list of available models that can be used for chat completions or other tasks. Returns either simple model list or detailed list with provider metadata based on X-PROVIDER-DATA header.", - "parameters": [ - { - "description": "Set to 'true' to include provider metadata in response", - "enum": [ - "true", - "false" - ], - "in": "header", - "name": "X-PROVIDER-DATA", - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "List of models with provider metadata (when X-PROVIDER-DATA=true)", - "schema": { - "$ref": "#/definitions/modelresponses.ModelWithProviderResponseList" - } - }, - "404": { - "description": "Models or providers not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Failed to retrieve models", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "List available models", - "tags": [ - "Chat Completions API" - ] - } - }, - "/v1/models/catalogs/{model_public_id}": { - "get": { - "description": "Retrieves detailed information about a model catalog entry by its public ID (supports IDs with slashes like openrouter/nova-lite-v1)", - "parameters": [ - { - "description": "Model Catalog Public ID (can contain slashes)", - "in": "path", - "name": "model_public_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Model catalog details", - "schema": { - "$ref": "#/definitions/modelresponses.ModelCatalogResponse" - } - }, - "400": { - "description": "Invalid request", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Model catalog not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal server error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get a model catalog entry", - "tags": [ - "Model API" - ] - } - }, - "/v1/models/providers": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Retrieves a list of available model providers that can be used for inference.", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "List of providers", - "schema": { - "$ref": "#/definitions/modelresponses.ProviderResponseList" - } - }, - "500": { - "description": "Failed to retrieve providers", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "List model providers", - "tags": [ - "Model API" - ] - } - }, - "/v1/projects": { - "get": { - "description": "List all projects for the authenticated user", - "parameters": [ - { - "description": "Maximum number of projects to return", - "in": "query", - "name": "limit", - "type": "integer" - }, - { - "description": "Return projects after the given numeric ID", - "in": "query", - "name": "after", - "type": "string" - }, - { - "description": "Sort order (asc or desc)", - "in": "query", - "name": "order", - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/projectres.ProjectListResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "List projects", - "tags": [ - "Projects API" - ] - }, - "post": { - "consumes": [ - "application/json" - ], - "description": "Create a new project for grouping conversations", - "parameters": [ - { - "description": "Create project request", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/projectreq.CreateProjectRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/projectres.ProjectResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Create project", - "tags": [ - "Projects API" - ] - } - }, - "/v1/projects/{project_id}": { - "delete": { - "description": "Soft-delete a project", - "parameters": [ - { - "description": "Project ID", - "in": "path", - "name": "project_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/projectres.ProjectDeletedResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Delete project", - "tags": [ - "Projects API" - ] - }, - "get": { - "description": "Get a single project by ID", - "parameters": [ - { - "description": "Project ID", - "in": "path", - "name": "project_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/projectres.ProjectResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get project", - "tags": [ - "Projects API" - ] - }, - "patch": { - "consumes": [ - "application/json" - ], - "description": "Update project name, instruction, or archived status", - "parameters": [ - { - "description": "Project ID", - "in": "path", - "name": "project_id", - "required": true, - "type": "string" - }, - { - "description": "Update request", - "in": "body", - "name": "request", - "required": true, - "schema": { - "$ref": "#/definitions/projectreq.UpdateProjectRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/projectres.ProjectResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Update project", - "tags": [ - "Projects API" - ] - } - }, - "/v1/prompt-templates/{key}": { - "get": { - "consumes": [ - "application/json" - ], - "description": "Get a prompt template by its unique template key (public endpoint)", - "parameters": [ - { - "description": "Template Key", - "in": "path", - "name": "key", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/prompttemplatehandler.PromptTemplateResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Get a prompt template by key", - "tags": [ - "Prompt Templates" - ] - } - }, - "/v1/public/shares/{slug}": { - "get": { - "description": "Retrieves a publicly shared conversation or message by its slug", - "parameters": [ - { - "description": "Share slug", - "in": "path", - "name": "slug", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Public share content", - "schema": { - "$ref": "#/definitions/shareresponses.PublicShareResponse" - } - }, - "404": { - "description": "Share not found or revoked", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "410": { - "description": "Share has been revoked", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "summary": "Get a public share by slug", - "tags": [ - "Public Shares API" - ] - }, - "head": { - "description": "Checks if a share exists and is accessible (for preloading)", - "parameters": [ - { - "description": "Share slug", - "in": "path", - "name": "slug", - "required": true, - "type": "string" - } - ], - "responses": { - "200": { - "description": "Share exists and is accessible" - }, - "404": { - "description": "Share not found" - }, - "410": { - "description": "Share has been revoked" - } - }, - "summary": "Check if a public share exists", - "tags": [ - "Public Shares API" - ] - } - }, - "/v1/readyz": { - "get": { - "description": "Returns the readiness status of the API server. Indicates if the service is ready to accept traffic.", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Readiness status ready", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Readiness check endpoint", - "tags": [ - "Server API" - ] - } - }, - "/v1/realtime/sessions": { - "get": { - "description": "Lists all active sessions for the current user", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Realtime_sessionres.ListSessionsResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/Realtime_responses.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/Realtime_responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "List realtime sessions", - "tags": [ - "Realtime API" - ] - }, - "post": { - "consumes": [ - "application/json" - ], - "description": "Creates a new realtime session with LiveKit token. No request body required.", - "produces": [ - "application/json" - ], - "responses": { - "201": { - "description": "Created", - "schema": { - "$ref": "#/definitions/Realtime_sessionres.SessionResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/Realtime_responses.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/Realtime_responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Create a realtime session", - "tags": [ - "Realtime API" - ] - } - }, - "/v1/realtime/sessions/{id}": { - "delete": { - "description": "Ends a session and invalidates its token. Users can only delete their own sessions.", - "parameters": [ - { - "description": "Session ID", - "in": "path", - "name": "id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Realtime_sessionres.DeleteSessionResponse" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "$ref": "#/definitions/Realtime_responses.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/Realtime_responses.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/Realtime_responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Delete a realtime session", - "tags": [ - "Realtime API" - ] - }, - "get": { - "description": "Retrieves a specific session by ID. Users can only access their own sessions.", - "parameters": [ - { - "description": "Session ID", - "in": "path", - "name": "id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/Realtime_sessionres.SessionResponse" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "$ref": "#/definitions/Realtime_responses.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/Realtime_responses.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/Realtime_responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get a realtime session", - "tags": [ - "Realtime API" - ] - } - }, - "/v1/shares": { - "get": { - "description": "Lists all shares (active and revoked) for the authenticated user across all conversations", - "parameters": [ - { - "default": true, - "description": "Include revoked shares", - "in": "query", - "name": "include_revoked", - "type": "boolean" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "List of shares", - "schema": { - "$ref": "#/definitions/shareresponses.ShareListResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "List all shares for the authenticated user", - "tags": [ - "Shares API" - ] - } - }, - "/v1/shares/{share_id}": { - "delete": { - "description": "Revokes an active share by its ID, making it inaccessible", - "parameters": [ - { - "description": "Share public ID", - "in": "path", - "name": "share_id", - "required": true, - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Share revoked successfully", - "schema": { - "$ref": "#/definitions/shareresponses.ShareDeletedResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "403": { - "description": "Forbidden", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "404": { - "description": "Share not found", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Revoke a share by share ID", - "tags": [ - "Shares API" - ] - } - }, - "/v1/usage/me": { - "get": { - "description": "Returns token usage summary for the authenticated user within a date range", - "parameters": [ - { - "description": "Start date (YYYY-MM-DD), defaults to 30 days ago", - "in": "query", - "name": "start_date", - "type": "string" - }, - { - "description": "End date (YYYY-MM-DD), defaults to today", - "in": "query", - "name": "end_date", - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/tokenusage.UsageResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get current user's token usage", - "tags": [ - "Usage" - ] - } - }, - "/v1/usage/me/daily": { - "get": { - "description": "Returns daily aggregated token usage for the authenticated user", - "parameters": [ - { - "description": "Start date (YYYY-MM-DD), defaults to 30 days ago", - "in": "query", - "name": "start_date", - "type": "string" - }, - { - "description": "End date (YYYY-MM-DD), defaults to today", - "in": "query", - "name": "end_date", - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "items": { - "$ref": "#/definitions/tokenusage.DailyAggregate" - }, - "type": "array" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get current user's daily token usage", - "tags": [ - "Usage" - ] - } - }, - "/v1/usage/projects/{id}": { - "get": { - "description": "Returns token usage summary for a specific project", - "parameters": [ - { - "description": "Project ID", - "in": "path", - "name": "id", - "required": true, - "type": "string" - }, - { - "description": "Start date (YYYY-MM-DD), defaults to 30 days ago", - "in": "query", - "name": "start_date", - "type": "string" - }, - { - "description": "End date (YYYY-MM-DD), defaults to today", - "in": "query", - "name": "end_date", - "type": "string" - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/tokenusage.UsageResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "404": { - "description": "Not Found", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get project's token usage", - "tags": [ - "Usage" - ] - } - }, - "/v1/users/me/settings": { - "get": { - "description": "Retrieve current user's settings including memory preferences", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/usersettingshandler.UserSettingsResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get user settings", - "tags": [ - "User Settings" - ] - }, - "patch": { - "consumes": [ - "application/json" - ], - "description": "Update current user's settings (partial update supported)", - "parameters": [ - { - "description": "Settings to update", - "in": "body", - "name": "settings", - "required": true, - "schema": { - "$ref": "#/definitions/usersettings.UpdateRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/usersettingshandler.UserSettingsResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Update user settings", - "tags": [ - "User Settings" - ] - } - }, - "/v1/users/me/settings/preferences": { - "get": { - "description": "Retrieve current user's preferences only", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/usersettingshandler.PreferencesResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Get user preferences", - "tags": [ - "User Settings" - ] - }, - "patch": { - "consumes": [ - "application/json" - ], - "description": "Update current user's preferences only", - "parameters": [ - { - "description": "Preferences to update", - "in": "body", - "name": "preferences", - "required": true, - "schema": { - "$ref": "#/definitions/usersettingshandler.UpdatePreferencesRequest" - } - } - ], - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/usersettingshandler.PreferencesResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "401": { - "description": "Unauthorized", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/responses.ErrorResponse" - } - } - }, - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "Update user preferences", - "tags": [ - "User Settings" - ] - } - }, - "/v1/version": { - "get": { - "description": "Returns the current build version of the API server and environment reload timestamp.", - "produces": [ - "application/json" - ], - "responses": { - "200": { - "description": "Version information including version number and environment reload timestamp", - "schema": { - "additionalProperties": { - "type": "string" - }, - "type": "object" - } - } - }, - "summary": "Get API build version", - "tags": [ - "Server API" - ] - } - } - }, - "securityDefinitions": { - "BearerAuth": { - "description": "Type \"Bearer\" followed by a space and JWT token.", - "in": "header", - "name": "Authorization", - "type": "apiKey" - } - }, - "swagger": "2.0", - "tags": [ - { - "name": "Authentication API" - }, - { - "name": "Server API" - }, - { - "name": "Model API" - }, - { - "name": "Admin Model API" - }, - { - "name": "Admin Provider API" - }, - { - "name": "Chat Completions API" - }, - { - "name": "Conversations API" - }, - { - "name": "Projects API" - }, - { - "name": "MCP API" - }, - { - "description": "Model Context Protocol tools", - "name": "MCP Tools" - }, - { - "description": "Realtime API for audio/video communication via LiveKit", - "name": "Realtime API" - } - ] -} \ No newline at end of file diff --git a/apps/platform/api/server.yaml b/apps/platform/api/server.yaml deleted file mode 100644 index be9a115d..00000000 --- a/apps/platform/api/server.yaml +++ /dev/null @@ -1,6769 +0,0 @@ -basePath: / -definitions: - authhandler.AccessTokenResponse: - properties: - access_token: - type: string - expires_at: - type: string - expires_in: - type: integer - refresh_token: - type: string - token_type: - type: string - type: object - authhandler.GetMeResponse: - properties: - auth_method: - type: string - email: - type: string - id: - type: string - is_admin: - type: boolean - name: - type: string - roles: - items: - type: string - type: array - subject: - type: string - username: - type: string - type: object - chatrequests.ChatCompletionRequest: - properties: - chat_template_kwargs: - additionalProperties: {} - description: |- - ChatTemplateKwargs provides a way to add non-standard parameters to the request body. - Additional kwargs to pass to the template renderer. Will be accessible by the chat template. - Such as think mode for qwen3. "chat_template_kwargs": {"enable_thinking": false} - https://qwen.readthedocs.io/en/latest/deployment/vllm.html#thinking-non-thinking-modes - type: object - conversation: - allOf: - - $ref: '#/definitions/chatrequests.ConversationReference' - description: |- - Conversation can be either a string (conversation ID) or a conversation object - Items from this conversation are prepended to Messages for this response request. - Input items and output items from this response are automatically added to this conversation after completion. - deep_research: - description: |- - DeepResearch enables the Deep Research mode which uses a specialized prompt - for conducting in-depth investigations with tool usage. - Requires a model with supports_reasoning: true capability. - type: boolean - enable_thinking: - description: |- - EnableThinking controls whether reasoning/thinking capabilities should be used. - Defaults to true. When set to false for a model with supports_reasoning: true - and an instruct model configured, the instruct model will be used instead. - type: boolean - frequency_penalty: - type: number - function_call: - description: 'Deprecated: use ToolChoice instead.' - functions: - description: 'Deprecated: use Tools instead.' - items: - $ref: '#/definitions/openai.FunctionDefinition' - type: array - guided_choice: - description: |- - GuidedChoice is a vLLM-specific extension that restricts the model's output - to one of the predefined string choices provided in this field. This feature - is used to constrain the model's responses to a controlled set of options, - ensuring predictable and consistent outputs in scenarios where specific - choices are required. - items: - type: string - type: array - logit_bias: - additionalProperties: - type: integer - description: |- - LogitBias is must be a token id string (specified by their token ID in the tokenizer), not a word string. - incorrect: `"logit_bias":{"You": 6}`, correct: `"logit_bias":{"1639": 6}` - refs: https://platform.openai.com/docs/api-reference/chat/create#chat/create-logit_bias - type: object - logprobs: - description: |- - LogProbs indicates whether to return log probabilities of the output tokens or not. - If true, returns the log probabilities of each output token returned in the content of message. - This option is currently not available on the gpt-4-vision-preview model. - type: boolean - max_completion_tokens: - description: |- - MaxCompletionTokens An upper bound for the number of tokens that can be generated for a completion, - including visible output tokens and reasoning tokens https://platform.openai.com/docs/guides/reasoning - type: integer - max_tokens: - description: |- - MaxTokens The maximum number of tokens that can be generated in the chat completion. - This value can be used to control costs for text generated via API. - Deprecated: use MaxCompletionTokens. Not compatible with o1-series models. - refs: https://platform.openai.com/docs/api-reference/chat/create#chat-create-max_tokens - type: integer - messages: - items: - $ref: '#/definitions/openai.ChatCompletionMessage' - type: array - metadata: - additionalProperties: - type: string - description: Metadata to store with the completion. - type: object - model: - type: string - "n": - type: integer - parallel_tool_calls: - description: 'Disable the default behavior of parallel tool calls by setting - it: false.' - prediction: - allOf: - - $ref: '#/definitions/openai.Prediction' - description: Configuration for a predicted output. - presence_penalty: - type: number - reasoning_effort: - description: Controls effort on reasoning for reasoning models. It can be - set to "low", "medium", or "high". - type: string - repetition_penalty: - type: number - response_format: - $ref: '#/definitions/openai.ChatCompletionResponseFormat' - safety_identifier: - description: |- - A stable identifier used to help detect users of your application that may be violating OpenAI's usage policies. - The IDs should be a string that uniquely identifies each user. - We recommend hashing their username or email address, in order to avoid sending us any identifying information. - https://platform.openai.com/docs/api-reference/chat/create#chat_create-safety_identifier - type: string - seed: - type: integer - service_tier: - allOf: - - $ref: '#/definitions/openai.ServiceTier' - description: Specifies the latency tier to use for processing the request. - stop: - items: - type: string - type: array - store: - description: Store controls whether the latest input and generated response - should be persisted - type: boolean - store_reasoning: - description: StoreReasoning controls whether reasoning content (if present) - should also be persisted - type: boolean - stream: - type: boolean - stream_options: - allOf: - - $ref: '#/definitions/openai.StreamOptions' - description: 'Options for streaming response. Only set this when you set stream: - true.' - temperature: - type: number - tool_choice: - description: This can be either a string or an ToolChoice object. - tools: - items: - $ref: '#/definitions/openai.Tool' - type: array - top_k: - type: integer - top_logprobs: - description: |- - TopLogProbs is an integer between 0 and 5 specifying the number of most likely tokens to return at each - token position, each with an associated log probability. - logprobs must be set to true if this parameter is used. - type: integer - top_p: - type: number - user: - type: string - verbosity: - description: |- - Verbosity determines how many output tokens are generated. Lowering the number of - tokens reduces overall latency. It can be set to "low", "medium", or "high". - Note: This field is only confirmed to work with gpt-5, gpt-5-mini and gpt-5-nano. - Also, it is not in the API reference of chat completion at the time of writing, - though it is supported by the API. - type: string - type: object - chatrequests.ConversationReference: - type: object - chatresponses.ChatCompletionResponse: - properties: - choices: - items: - $ref: '#/definitions/openai.ChatCompletionChoice' - type: array - conversation: - $ref: '#/definitions/chatresponses.ConversationContext' - created: - type: integer - id: - type: string - model: - type: string - object: - type: string - prompt_filter_results: - items: - $ref: '#/definitions/openai.PromptFilterResult' - type: array - service_tier: - $ref: '#/definitions/openai.ServiceTier' - system_fingerprint: - type: string - trimmed: - description: True if messages were trimmed to fit context - type: boolean - usage: - $ref: '#/definitions/openai.Usage' - type: object - chatresponses.ConversationContext: - properties: - id: - description: The unique ID of the conversation - type: string - title: - description: The title of the conversation (optional) - type: string - type: object - conversation.Annotation: - properties: - bounding_box: - allOf: - - $ref: '#/definitions/conversation.BBox' - description: Bounding box for image/PDF annotations - confidence: - description: Citation confidence score (0.0-1.0) - type: number - container_id: - description: Document container reference - type: string - end_index: - description: End position in text - type: integer - file_id: - description: For file citations - type: string - filename: - description: File name for citations - type: string - index: - description: Citation index - type: integer - page_number: - description: Page reference for documents - type: integer - quote: - description: Actual quoted text from source - type: string - start_index: - description: Start position in text - type: integer - text: - description: Display text - type: string - type: - description: '"file_citation", "url_citation", "file_path", etc.' - type: string - url: - description: For URL citations - type: string - type: object - conversation.AudioContent: - properties: - data: - description: Base64 encoded audio data - type: string - format: - description: 'Audio format: mp3, wav, pcm16, etc.' - type: string - id: - type: string - transcript: - description: Text transcription of audio - type: string - type: object - conversation.BBox: - properties: - height: - type: number - width: - type: number - x: - type: number - "y": - type: number - type: object - conversation.CodeContent: - properties: - code: - description: Code content - type: string - error: - description: Execution error - type: string - execution_id: - description: Execution session ID - type: string - exit_code: - description: Process exit code - type: integer - language: - description: Programming language - type: string - metadata: - additionalProperties: {} - description: Additional metadata - type: object - output: - description: Execution output - type: string - type: object - conversation.ComputerAction: - properties: - action: - description: 'Action type: "click", "type", "key", "scroll", "move", etc.' - type: string - coordinates: - allOf: - - $ref: '#/definitions/conversation.Coordinates' - description: Screen coordinates for mouse actions - key: - description: Key for keyboard actions - type: string - metadata: - additionalProperties: {} - description: Additional action metadata - type: object - scroll_delta: - allOf: - - $ref: '#/definitions/conversation.ScrollDelta' - description: Scroll amount - text: - description: Text for typing actions - type: string - type: object - conversation.Content: - properties: - audio: - allOf: - - $ref: '#/definitions/conversation.AudioContent' - description: Audio content for speech - code: - allOf: - - $ref: '#/definitions/conversation.CodeContent' - description: Code block with execution metadata - computer_action: - allOf: - - $ref: '#/definitions/conversation.ComputerAction' - description: Computer interaction details - computer_screenshot: - allOf: - - $ref: '#/definitions/conversation.ScreenshotContent' - description: Screenshot from computer use - file: - allOf: - - $ref: '#/definitions/conversation.FileContent' - description: File content - finish_reason: - description: Finish reason - type: string - function_call: - allOf: - - $ref: '#/definitions/conversation.FunctionCall' - description: Function call content (deprecated, use tool_calls) - function_call_output: - allOf: - - $ref: '#/definitions/conversation.FunctionCallOut' - description: Function call output - image: - allOf: - - $ref: '#/definitions/conversation.ImageContent' - description: Image content - input_audio: - allOf: - - $ref: '#/definitions/conversation.InputAudio' - description: User audio input - output_text: - allOf: - - $ref: '#/definitions/conversation.OutputText' - description: AI output text (with annotations) - refusal: - description: Model refusal message - type: string - summary_text: - description: Summary content - type: string - thinking: - description: Internal reasoning (o1 models) - type: string - tool_call_id: - description: Tool call ID (for tool responses) - type: string - tool_calls: - description: Tool calls (for assistant messages) - items: - $ref: '#/definitions/conversation.ToolCall' - type: array - type: - type: string - type: object - conversation.Coordinates: - properties: - x: - type: integer - "y": - type: integer - type: object - conversation.FileContent: - properties: - file_id: - type: string - mime_type: - type: string - name: - type: string - size: - type: integer - type: object - conversation.FunctionCall: - properties: - arguments: - description: JSON-encoded arguments - type: string - id: - description: Call ID - type: string - name: - description: Function name - type: string - type: object - conversation.FunctionCallOut: - properties: - call_id: - description: ID of the function call this responds to - type: string - output: - description: String output from the function - type: string - type: object - conversation.ImageContent: - properties: - detail: - description: '"low", "high", "auto"' - type: string - file_id: - type: string - url: - type: string - type: object - conversation.IncompleteDetails: - properties: - error: - description: Error message if applicable - type: string - reason: - description: '"max_tokens", "content_filter", "tool_calls", etc.' - type: string - type: object - conversation.InputAudio: - properties: - data: - description: Base64 encoded audio data - type: string - format: - description: 'Audio format: mp3, wav, pcm16, etc.' - type: string - transcript: - description: Optional text transcription - type: string - type: object - conversation.Item: - properties: - acknowledged_safety_checks: - description: For computer call outputs - items: - $ref: '#/definitions/conversation.SafetyCheck' - type: array - action: - additionalProperties: true - description: For computer/shell actions - type: object - approval_request_id: - description: For MCP approval - type: string - approve: - description: For MCP approval response - type: boolean - arguments: - description: For tool calls (JSON string) - type: string - branch: - description: Branch identifier (MAIN, EDIT_1, etc.) - type: string - call_id: - description: OpenAI-compatible fields for specific item types - type: string - commands: - description: For shell calls - items: - type: string - type: array - completed_at: - type: string - content: - items: - $ref: '#/definitions/conversation.Content' - type: array - created_at: - type: string - error: - description: For failed calls - type: string - id: - type: string - incomplete_at: - type: string - incomplete_details: - $ref: '#/definitions/conversation.IncompleteDetails' - max_output_length: - description: For shell calls - type: integer - name: - description: For MCP tool calls - tool name - type: string - object: - description: Always "conversation.item" for OpenAI compatibility - type: string - operation: - additionalProperties: true - description: For patch operations - type: object - output: - description: For tool call outputs - type: string - pending_safety_checks: - description: For computer calls - items: - $ref: '#/definitions/conversation.SafetyCheck' - type: array - rated_at: - description: When rating was given - type: string - rating: - allOf: - - $ref: '#/definitions/conversation.ItemRating' - description: User feedback/rating - rating_comment: - description: Optional comment with rating - type: string - reason: - description: For MCP approval reason - type: string - role: - $ref: '#/definitions/conversation.ItemRole' - sequence_number: - description: Order within branch - type: integer - server_label: - description: For MCP calls - type: string - shell_outputs: - description: For shell call outputs - items: - $ref: '#/definitions/conversation.ShellOutput' - type: array - status: - $ref: '#/definitions/conversation.ItemStatus' - tools: - description: For mcp_list_tools - items: - $ref: '#/definitions/conversation.McpTool' - type: array - type: - $ref: '#/definitions/conversation.ItemType' - type: object - conversation.ItemRating: - enum: - - like - - unlike - type: string - x-enum-comments: - ItemRatingLike: Positive feedback (like) - ItemRatingUnlike: Negative feedback (unlike) - x-enum-varnames: - - ItemRatingLike - - ItemRatingUnlike - conversation.ItemRole: - enum: - - system - - user - - assistant - - tool - - developer - - critic - - discriminator - - unknown - type: string - x-enum-comments: - ItemRoleCritic: For critique/evaluation workflows - ItemRoleDeveloper: System-level instructions (OpenAI replacement for system) - ItemRoleDiscriminator: For adversarial/validation workflows - ItemRoleUnknown: Fallback for unrecognized roles - x-enum-varnames: - - ItemRoleSystem - - ItemRoleUser - - ItemRoleAssistant - - ItemRoleTool - - ItemRoleDeveloper - - ItemRoleCritic - - ItemRoleDiscriminator - - ItemRoleUnknown - conversation.ItemStatus: - enum: - - incomplete - - in_progress - - completed - - failed - - cancelled - - searching - - generating - - calling - - streaming - - rate_limited - type: string - x-enum-comments: - ItemStatusCalling: Function/tool call in progress - ItemStatusCancelled: Cancelled by user or system - ItemStatusCompleted: Successfully finished - ItemStatusFailed: Failed with error - ItemStatusGenerating: Image generation in progress - ItemStatusInProgress: Currently processing - ItemStatusIncomplete: Not started or partially complete (OpenAI uses this instead - of "pending") - ItemStatusRateLimited: Rate limit hit - ItemStatusSearching: File/web search in progress - ItemStatusStreaming: Streaming response in progress - x-enum-varnames: - - ItemStatusIncomplete - - ItemStatusInProgress - - ItemStatusCompleted - - ItemStatusFailed - - ItemStatusCancelled - - ItemStatusSearching - - ItemStatusGenerating - - ItemStatusCalling - - ItemStatusStreaming - - ItemStatusRateLimited - conversation.ItemType: - enum: - - message - - function_call - - function_call_output - - reasoning - - file_search_call - - web_search_call - - image_generation_call - - image_edit_call - - computer_call - - computer_call_output - - code_interpreter_call - - local_shell_call - - local_shell_call_output - - shell_call - - shell_call_output - - apply_patch_call - - apply_patch_call_output - - mcp_list_tools - - mcp_approval_request - - mcp_approval_response - - mcp_call - - custom_tool_call - - custom_tool_call_output - - file_search - - web_search - - code_interpreter - - computer_use - - mcp_item - - image_generation - type: string - x-enum-comments: - ItemTypeCodeInterpreter: 'Legacy: maps to code_interpreter_call' - ItemTypeComputerUse: 'Legacy: maps to computer_call' - ItemTypeFileSearch: 'Legacy: maps to file_search_call' - ItemTypeImageGeneration: 'Legacy: maps to image_generation_call' - ItemTypeMCPItem: 'Legacy: maps to mcp_call' - ItemTypeReasoning: For o1/reasoning models - ItemTypeWebSearch: 'Legacy: maps to web_search_call' - x-enum-varnames: - - ItemTypeMessage - - ItemTypeFunctionCall - - ItemTypeFunctionCallOut - - ItemTypeReasoning - - ItemTypeFileSearchCall - - ItemTypeWebSearchCall - - ItemTypeImageGenerationCall - - ItemTypeImageEditCall - - ItemTypeComputerCall - - ItemTypeComputerCallOutput - - ItemTypeCodeInterpreterCall - - ItemTypeLocalShellCall - - ItemTypeLocalShellCallOutput - - ItemTypeShellCall - - ItemTypeShellCallOutput - - ItemTypeApplyPatchCall - - ItemTypeApplyPatchCallOutput - - ItemTypeMcpListTools - - ItemTypeMcpApprovalRequest - - ItemTypeMcpApprovalResponse - - ItemTypeMcpCall - - ItemTypeCustomToolCall - - ItemTypeCustomToolCallOutput - - ItemTypeFileSearch - - ItemTypeWebSearch - - ItemTypeCodeInterpreter - - ItemTypeComputerUse - - ItemTypeMCPItem - - ItemTypeImageGeneration - conversation.LogProb: - properties: - bytes: - items: - type: integer - type: array - logprob: - type: number - token: - type: string - top_logprobs: - items: - $ref: '#/definitions/conversation.TopLogProb' - type: array - type: object - conversation.McpTool: - properties: - annotations: {} - description: - type: string - input_schema: {} - name: - type: string - type: object - conversation.OutputText: - properties: - annotations: - description: Required for OpenAI compatibility - items: - $ref: '#/definitions/conversation.Annotation' - type: array - logprobs: - description: Token probabilities - items: - $ref: '#/definitions/conversation.LogProb' - type: array - text: - type: string - type: object - conversation.SafetyCheck: - properties: - reason: - type: string - type: - type: string - type: object - conversation.ScreenshotContent: - properties: - description: - description: Optional description - type: string - height: - description: Image height in pixels - type: integer - image_data: - description: Base64 encoded image data - type: string - image_url: - description: URL to screenshot image - type: string - timestamp: - description: Unix timestamp when screenshot was taken - type: integer - width: - description: Image width in pixels - type: integer - type: object - conversation.ScrollDelta: - properties: - x: - type: integer - "y": - type: integer - type: object - conversation.ShellOutput: - properties: - type: - description: stdout, stderr, exit_code - type: string - value: - type: string - type: object - conversation.ToolCall: - properties: - function: - $ref: '#/definitions/conversation.FunctionCall' - id: - type: string - type: - description: '"function", "file_search", "code_interpreter"' - type: string - type: object - conversation.TopLogProb: - properties: - bytes: - items: - type: integer - type: array - logprob: - type: number - token: - type: string - type: object - conversationhandler.ActivateBranchResponse: - properties: - active_branch: - type: string - message: - type: string - type: object - conversationhandler.BranchResponse: - properties: - created_at: - type: integer - description: - type: string - forked_at: - type: integer - forked_from_item_id: - type: string - is_active: - type: boolean - item_count: - type: integer - name: - type: string - parent_branch: - type: string - updated_at: - type: integer - type: object - conversationhandler.CreateBranchRequest: - properties: - description: - type: string - fork_from_item_id: - type: string - name: - type: string - parent_branch: - type: string - required: - - name - type: object - conversationhandler.DeleteItemResponse: - properties: - branch: - description: Always "MAIN" after swap - type: string - branch_created: - type: boolean - deleted: - type: boolean - old_main_backup: - description: Backup name for old MAIN - type: string - type: object - conversationhandler.EditMessageRequest: - properties: - content: - type: string - regenerate: - description: 'Auto-trigger new response (default: true)' - type: boolean - required: - - content - type: object - conversationhandler.EditMessageResponse: - properties: - branch: - description: Always "MAIN" after swap - type: string - branch_created: - type: boolean - new_branch: - $ref: '#/definitions/conversationhandler.BranchResponse' - old_main_backup: - description: Backup name for old MAIN - type: string - user_item: - $ref: '#/definitions/conversation.Item' - type: object - conversationhandler.ListBranchesResponse: - properties: - active_branch: - type: string - data: - items: - $ref: '#/definitions/conversationhandler.BranchResponse' - type: array - object: - description: '"list"' - type: string - type: object - conversationhandler.RegenerateMessageRequest: - properties: - max_tokens: - description: Override max tokens - type: integer - model: - description: Override model - type: string - temperature: - description: Override temperature - type: number - type: object - conversationhandler.RegenerateMessageResponse: - properties: - branch: - description: Always "MAIN" after swap - type: string - branch_created: - type: boolean - new_branch: - $ref: '#/definitions/conversationhandler.BranchResponse' - old_main_backup: - description: Backup name for old MAIN - type: string - user_item_id: - type: string - type: object - conversationrequests.CreateConversationRequest: - properties: - items: - items: - $ref: '#/definitions/conversation.Item' - type: array - metadata: - additionalProperties: - type: string - type: object - project_id: - type: string - referrer: - type: string - title: - type: string - type: object - conversationrequests.CreateItemsRequest: - properties: - items: - items: - $ref: '#/definitions/conversation.Item' - type: array - required: - - items - type: object - conversationrequests.UpdateConversationRequest: - properties: - metadata: - additionalProperties: - type: string - type: object - project_id: - type: string - referrer: - type: string - title: - type: string - type: object - conversationrequests.UpdateItemByCallIDRequest: - properties: - arguments: - description: JSON string of arguments - type: string - error: - description: Error message if status is "failed" - type: string - name: - description: Tool info fields (optional - already set on creation, but can - be updated) - type: string - output: - description: Result fields - type: string - server_label: - description: MCP server label - type: string - status: - description: Required fields - type: string - required: - - status - type: object - conversationresponses.BulkConversationsDeletedResponse: - properties: - deleted: - type: boolean - deleted_count: - type: integer - object: - type: string - type: object - conversationresponses.ConversationDeletedResponse: - properties: - deleted: - type: boolean - id: - type: string - object: - type: string - type: object - conversationresponses.ConversationItemCreatedResponse: - properties: - data: - items: - $ref: '#/definitions/conversation.Item' - type: array - first_id: - type: string - has_more: - type: boolean - last_id: - type: string - object: - type: string - type: object - conversationresponses.ConversationListResponse: - properties: - data: - items: - $ref: '#/definitions/conversationresponses.ConversationResponse' - type: array - first_id: - type: string - has_more: - type: boolean - last_id: - type: string - object: - type: string - total: - type: integer - type: object - conversationresponses.ConversationResponse: - properties: - created_at: - type: integer - id: - type: string - metadata: - additionalProperties: - type: string - type: object - object: - type: string - project_id: - type: string - referrer: - type: string - title: - type: string - updated_at: - type: integer - type: object - conversationresponses.ItemListResponse: - properties: - data: - items: - $ref: '#/definitions/conversation.Item' - type: array - first_id: - type: string - has_more: - type: boolean - last_id: - type: string - object: - type: string - type: object - conversationresponses.ItemResponse: - properties: - acknowledged_safety_checks: - description: For computer call outputs - items: - $ref: '#/definitions/conversation.SafetyCheck' - type: array - action: - additionalProperties: true - description: For computer/shell actions - type: object - approval_request_id: - description: For MCP approval - type: string - approve: - description: For MCP approval response - type: boolean - arguments: - description: For tool calls (JSON string) - type: string - branch: - description: Branch identifier (MAIN, EDIT_1, etc.) - type: string - call_id: - description: OpenAI-compatible fields for specific item types - type: string - commands: - description: For shell calls - items: - type: string - type: array - completed_at: - type: string - content: - items: - $ref: '#/definitions/conversation.Content' - type: array - created_at: - type: string - error: - description: For failed calls - type: string - id: - type: string - incomplete_at: - type: string - incomplete_details: - $ref: '#/definitions/conversation.IncompleteDetails' - max_output_length: - description: For shell calls - type: integer - name: - description: For MCP tool calls - tool name - type: string - object: - description: Always "conversation.item" for OpenAI compatibility - type: string - operation: - additionalProperties: true - description: For patch operations - type: object - output: - description: For tool call outputs - type: string - pending_safety_checks: - description: For computer calls - items: - $ref: '#/definitions/conversation.SafetyCheck' - type: array - rated_at: - description: When rating was given - type: string - rating: - allOf: - - $ref: '#/definitions/conversation.ItemRating' - description: User feedback/rating - rating_comment: - description: Optional comment with rating - type: string - reason: - description: For MCP approval reason - type: string - role: - $ref: '#/definitions/conversation.ItemRole' - sequence_number: - description: Order within branch - type: integer - server_label: - description: For MCP calls - type: string - shell_outputs: - description: For shell call outputs - items: - $ref: '#/definitions/conversation.ShellOutput' - type: array - status: - $ref: '#/definitions/conversation.ItemStatus' - tools: - description: For mcp_list_tools - items: - $ref: '#/definitions/conversation.McpTool' - type: array - type: - $ref: '#/definitions/conversation.ItemType' - type: object - image.ImageData: - description: Single generated image data - properties: - b64_json: - description: |- - B64JSON is the base64-encoded image data. - Present when response_format="b64_json". - type: string - id: - description: ID is the Jan media ID (jan_xxxxx) for the stored image. - example: jan_abc123 - type: string - revised_prompt: - description: RevisedPrompt is the revised prompt used for generation, if the - provider modified it. - type: string - url: - description: |- - URL is the presigned URL to the generated image. - Present when response_format="url". - example: https://media.jan.ai/images/jan_abc123.png?sig=... - type: string - type: object - image.ImageEditRequest: - properties: - cfg_scale: - description: CfgScale is classifier-free guidance scale. - example: 1 - type: number - conversation_id: - description: ConversationID optionally links this edit to a conversation. - example: conv_abc123 - type: string - image: - allOf: - - $ref: '#/definitions/image.ImageInput' - description: Image is the input image to edit. Required. - mask: - allOf: - - $ref: '#/definitions/image.ImageInput' - description: Mask is an optional mask for inpainting. - model: - description: Model specifies the image edit model. Optional. - example: qwen-image-edit - type: string - "n": - description: N is the number of images to generate (only 1 supported by most - providers). - example: 1 - type: integer - negative_prompt: - description: NegativePrompt describes what to avoid. - example: ' ' - type: string - prompt: - description: Prompt is the text instruction describing the edit. Required. - example: add golden sunglasses - type: string - provider_id: - description: ProviderID optionally overrides the default image edit provider. - example: prov_abc123 - type: string - response_format: - description: ResponseFormat determines output format ("url" or "b64_json"). - example: b64_json - type: string - sampler: - description: Sampler selects the sampling algorithm. - example: euler - type: string - scheduler: - description: Scheduler selects the scheduler. - example: simple - type: string - seed: - description: Seed sets random seed (-1 for random). - example: -1 - type: integer - size: - description: Size specifies the output size ("original" or "WIDTHxHEIGHT"). - example: original - type: string - steps: - description: Steps controls sampling steps. - example: 4 - type: integer - store: - description: |- - Store controls whether to save the result to the conversation. - nil/true = store (default), false = don't store. - example: true - type: boolean - strength: - description: Strength controls edit intensity (0.0-1.0). - example: 1 - type: number - required: - - image - - prompt - type: object - image.ImageGenerationRequest: - description: OpenAI-compatible image generation request - properties: - cfg_scale: - description: CfgScale is a provider-specific parameter (z-image/Flux) for - guidance scale. - example: 7.5 - type: number - conversation_id: - description: ConversationID optionally links this generation to a conversation. - example: conv_abc123 - type: string - model: - description: |- - Model specifies the image generation model (e.g., "z-image", "flux-dev", "dall-e-3"). - If omitted, defaults to the configured default model. - example: z-image - type: string - "n": - description: 'N is the number of images to generate (1-10, default: 1).' - example: 1 - type: integer - num_inference_steps: - description: NumInferenceSteps is a provider-specific parameter (z-image/Flux). - example: 20 - type: integer - prompt: - description: Prompt is the text description of the desired image. Required. - example: A serene mountain landscape at sunset - type: string - provider_id: - description: ProviderID optionally overrides the default image provider selection. - example: prov_abc123 - type: string - quality: - description: |- - Quality determines image quality. Valid values: "standard", "hd". - Default: "standard". - example: standard - type: string - response_format: - description: |- - ResponseFormat determines output format. Valid values: "url", "b64_json". - Default: "url". - example: url - type: string - size: - description: |- - Size specifies the dimensions of the generated image. - Supported sizes: "256x256", "512x512", "1024x1024", "1024x1792", "1792x1024". - Default: "1024x1024". - example: 1024x1024 - type: string - store: - description: |- - Store controls whether to save the result to the conversation. - nil/true = store (default), false = don't store. - example: true - type: boolean - style: - description: |- - Style influences the visual aesthetic. Valid values: "vivid", "natural". - Default: "natural". - example: natural - type: string - user: - description: User is an optional unique identifier representing the end-user - for abuse monitoring. - example: user-123 - type: string - required: - - prompt - type: object - image.ImageGenerationResponse: - description: OpenAI-compatible image generation response - properties: - created: - description: Created is the Unix timestamp of when the response was generated. - example: 1699000000 - type: integer - data: - description: Data contains the generated images. - items: - $ref: '#/definitions/image.ImageData' - type: array - usage: - allOf: - - $ref: '#/definitions/image.ImageUsage' - description: Usage contains token usage information for billing purposes. - type: object - image.ImageInput: - properties: - b64_json: - description: B64JSON is base64-encoded image data (no data URL prefix). - type: string - id: - description: ID is a Jan media ID (jan_*). - example: jan_abc123 - type: string - url: - description: URL is a remote URL to the image. - example: https://example.com/image.png - type: string - type: object - image.ImageUsage: - description: Token usage information for billing - properties: - input_tokens: - description: InputTokens is the estimated tokens for the prompt. - example: 100 - type: integer - input_tokens_details: - allOf: - - $ref: '#/definitions/image.InputTokensDetail' - description: InputTokensDetails provides a breakdown of input token types. - output_tokens: - description: OutputTokens is the estimated tokens for the generated images. - example: 1400 - type: integer - total_tokens: - description: TotalTokens is the sum of input and output tokens. - example: 1500 - type: integer - type: object - image.InputTokensDetail: - description: Breakdown of input token types - properties: - image_tokens: - description: ImageTokens is the number of tokens from input images (for edit/variation - operations). - example: 0 - type: integer - text_tokens: - description: TextTokens is the number of tokens from the text prompt. - example: 100 - type: integer - type: object - mcptool.UpdateMCPToolRequest: - properties: - category: - type: string - description: - type: string - disallowed_keywords: - description: Regex patterns - items: - type: string - type: array - is_active: - type: boolean - metadata: - additionalProperties: {} - type: object - type: object - mcptoolhandler.ActiveToolsResponse: - properties: - data: - items: - $ref: '#/definitions/mcptoolhandler.MCPToolResponse' - type: array - type: object - mcptoolhandler.ListResponse: - properties: - data: - items: - $ref: '#/definitions/mcptoolhandler.MCPToolResponse' - type: array - total: - type: integer - type: object - mcptoolhandler.MCPToolResponse: - properties: - category: - type: string - created_at: - type: string - description: - type: string - disallowed_keywords: - items: - type: string - type: array - id: - type: string - is_active: - type: boolean - metadata: - additionalProperties: {} - type: object - name: - type: string - public_id: - type: string - tool_key: - type: string - updated_at: - type: string - type: object - model.Architecture: - properties: - input_modalities: - items: - type: string - type: array - instruct_type: - description: nullable - type: string - modality: - description: '"text+image->text"' - type: string - output_modalities: - items: - type: string - type: array - tokenizer: - description: '"GPT" / "SentencePiece" / etc.' - type: string - type: object - model.ModelCatalogStatus: - enum: - - init - - filled - - updated - type: string - x-enum-comments: - ModelCatalogStatusFilled: may update from Provider like OpenRouter - ModelCatalogStatusInit: default status when creating entry - ModelCatalogStatusUpdated: manually updated by admin (cannot be auto-updated - anymore - x-enum-varnames: - - ModelCatalogStatusInit - - ModelCatalogStatusFilled - - ModelCatalogStatusUpdated - model.PriceLine: - properties: - amount_micro_usd: - description: e.g., 15000 -> $0.0150 - type: integer - currency: - description: '"USD" (fixed if you only bill in USD)' - type: string - unit: - $ref: '#/definitions/model.PriceUnit' - type: object - model.PriceUnit: - enum: - - per_1k_prompt_tokens - - per_1k_completion_tokens - - per_request - - per_image - - per_web_search - - per_internal_reasoning - type: string - x-enum-varnames: - - Per1KPromptTokens - - Per1KCompletionTokens - - PerRequest - - PerImage - - PerWebSearch - - PerInternalReasoning - model.Pricing: - properties: - lines: - description: 'flexible: add/remove units without schema churn' - items: - $ref: '#/definitions/model.PriceLine' - type: array - type: object - model.SupportedParameters: - properties: - default: - additionalProperties: - type: number - description: temperature/top_p/frequency_penalty, null allowed - type: object - names: - description: e.g., ["include_reasoning","max_tokens",...] - items: - type: string - type: array - type: object - model.TokenLimits: - properties: - context_length: - description: e.g., 400000 - type: integer - max_completion_tokens: - description: e.g., 128000 - type: integer - type: object - modelprompthandler.AssignRequest: - properties: - is_active: - type: boolean - priority: - type: integer - prompt_template_id: - type: string - template_key: - type: string - required: - - prompt_template_id - - template_key - type: object - modelprompthandler.EffectiveTemplateResponse: - properties: - source: - description: '"model_specific", "global_default", "hardcoded"' - type: string - template: - $ref: '#/definitions/modelprompthandler.FullPromptTemplateResponse' - type: object - modelprompthandler.EffectiveTemplatesResponse: - properties: - templates: - additionalProperties: - $ref: '#/definitions/modelprompthandler.EffectiveTemplateResponse' - type: object - type: object - modelprompthandler.FullPromptTemplateResponse: - properties: - category: - type: string - content: - type: string - description: - type: string - is_active: - type: boolean - is_system: - type: boolean - metadata: - additionalProperties: {} - type: object - name: - type: string - public_id: - type: string - template_key: - type: string - variables: - items: - type: string - type: array - version: - type: integer - type: object - modelprompthandler.ListResponse: - properties: - data: - items: - $ref: '#/definitions/modelprompthandler.ModelPromptTemplateResponse' - type: array - total: - type: integer - type: object - modelprompthandler.ModelPromptTemplateResponse: - properties: - created_at: - type: string - id: - type: string - is_active: - type: boolean - model_catalog_id: - type: string - priority: - type: integer - prompt_template: - $ref: '#/definitions/modelprompthandler.PromptTemplateResponse' - prompt_template_id: - type: string - template_key: - type: string - updated_at: - type: string - type: object - modelprompthandler.PromptTemplateResponse: - properties: - category: - type: string - description: - type: string - is_active: - type: boolean - name: - type: string - public_id: - type: string - template_key: - type: string - type: object - modelprompthandler.UpdateRequest: - properties: - is_active: - type: boolean - priority: - type: integer - prompt_template_id: - type: string - type: object - modelresponses.BulkOperationResponse: - properties: - failed_count: - type: integer - failed_models: - items: - type: string - type: array - skipped_count: - type: integer - total_checked: - type: integer - updated_count: - type: integer - type: object - modelresponses.EndpointResponse: - properties: - healthy: - type: boolean - priority: - type: integer - url: - type: string - weight: - type: integer - type: object - modelresponses.ModelCatalogResponse: - properties: - active: - type: boolean - architecture: - $ref: '#/definitions/model.Architecture' - context_length: - type: integer - created_at: - type: integer - description: - type: string - experimental: - type: boolean - extras: - additionalProperties: {} - type: object - family: - type: string - id: - type: string - is_moderated: - type: boolean - last_synced_at: - type: integer - model_display_name: - type: string - notes: - type: string - public_id: - type: string - requires_feature_flag: - type: string - status: - $ref: '#/definitions/model.ModelCatalogStatus' - supported_parameters: - $ref: '#/definitions/model.SupportedParameters' - supports_audio: - type: boolean - supports_browser: - type: boolean - supports_embeddings: - type: boolean - supports_images: - type: boolean - supports_instruct: - type: boolean - supports_reasoning: - type: boolean - supports_tools: - type: boolean - supports_video: - type: boolean - tags: - items: - type: string - type: array - updated_at: - type: integer - type: object - modelresponses.ModelResponse: - properties: - category: - type: string - category_order_number: - type: integer - created: - type: integer - id: - type: string - model_display_name: - type: string - model_order_number: - type: integer - object: - type: string - owned_by: - type: string - type: object - modelresponses.ModelResponseList: - properties: - data: - items: - $ref: '#/definitions/modelresponses.ModelResponse' - type: array - object: - type: string - type: object - modelresponses.ModelResponseWithProvider: - properties: - category: - type: string - category_order_number: - type: integer - created: - type: integer - id: - type: string - model_display_name: - type: string - model_order_number: - type: integer - object: - type: string - owned_by: - type: string - provider_id: - type: string - provider_name: - type: string - provider_vendor: - type: string - type: object - modelresponses.ModelWithProviderResponseList: - properties: - data: - items: - $ref: '#/definitions/modelresponses.ModelResponseWithProvider' - type: array - object: - type: string - type: object - modelresponses.ProviderModelResponse: - properties: - active: - type: boolean - category: - type: string - category_order_number: - type: integer - created_at: - type: integer - family: - type: string - id: - type: string - instruct_model_public_id: - description: Public ID of the instruct model to use when enable_thinking=false - type: string - model_catalog_id: - type: string - model_display_name: - type: string - model_order_number: - type: integer - model_public_id: - type: string - pricing: - $ref: '#/definitions/model.Pricing' - provider_id: - type: string - provider_original_model_id: - type: string - provider_vendor: - type: string - supports_audio: - type: boolean - supports_embeddings: - type: boolean - supports_images: - type: boolean - supports_instruct: - type: boolean - supports_reasoning: - type: boolean - supports_video: - type: boolean - token_limits: - $ref: '#/definitions/model.TokenLimits' - updated_at: - type: integer - type: object - modelresponses.ProviderResponse: - properties: - active: - type: boolean - base_url: - type: string - category: - type: string - default_provider_image_edit: - type: boolean - default_provider_image_generate: - type: boolean - endpoints: - items: - $ref: '#/definitions/modelresponses.EndpointResponse' - type: array - id: - type: string - metadata: - additionalProperties: - type: string - type: object - name: - type: string - vendor: - type: string - type: object - modelresponses.ProviderResponseList: - properties: - data: - items: - $ref: '#/definitions/modelresponses.ProviderResponse' - type: array - object: - type: string - type: object - modelresponses.ProviderWithModelCountResponse: - properties: - active: - type: boolean - base_url: - type: string - category: - type: string - default_provider_image_edit: - type: boolean - default_provider_image_generate: - type: boolean - endpoints: - items: - $ref: '#/definitions/modelresponses.EndpointResponse' - type: array - id: - type: string - metadata: - additionalProperties: - type: string - type: object - model_active_count: - type: integer - model_count: - type: integer - name: - type: string - vendor: - type: string - type: object - modelresponses.ProviderWithModelsResponse: - properties: - active: - type: boolean - base_url: - type: string - category: - type: string - default_provider_image_edit: - type: boolean - default_provider_image_generate: - type: boolean - endpoints: - items: - $ref: '#/definitions/modelresponses.EndpointResponse' - type: array - id: - type: string - metadata: - additionalProperties: - type: string - type: object - models: - items: - $ref: '#/definitions/modelresponses.ModelResponse' - type: array - name: - type: string - vendor: - type: string - type: object - openai.ChatCompletionChoice: - properties: - content_filter_results: - $ref: '#/definitions/openai.ContentFilterResults' - finish_reason: - allOf: - - $ref: '#/definitions/openai.FinishReason' - description: |- - FinishReason - stop: API returned complete message, - or a message terminated by one of the stop sequences provided via the stop parameter - length: Incomplete model output due to max_tokens parameter or token limit - function_call: The model decided to call a function - content_filter: Omitted content due to a flag from our content filters - null: API response still in progress or incomplete - index: - type: integer - logprobs: - $ref: '#/definitions/openai.LogProbs' - message: - $ref: '#/definitions/openai.ChatCompletionMessage' - type: object - openai.ChatCompletionMessage: - properties: - content: - type: string - function_call: - $ref: '#/definitions/openai.FunctionCall' - multiContent: - items: - $ref: '#/definitions/openai.ChatMessagePart' - type: array - name: - description: |- - This property isn't in the official documentation, but it's in - the documentation for the official library for python: - - https://github.com/openai/openai-python/blob/main/chatml.md - - https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb - type: string - reasoning_content: - description: |- - This property is used for the "reasoning" feature supported by deepseek-reasoner - which is not in the official documentation. - the doc from deepseek: - - https://api-docs.deepseek.com/api/create-chat-completion#responses - type: string - refusal: - type: string - role: - type: string - tool_call_id: - description: For Role=tool prompts this should be set to the ID given in the - assistant's prior request to call a tool. - type: string - tool_calls: - description: For Role=assistant prompts this may be set to the tool calls - generated by the model, such as function calls. - items: - $ref: '#/definitions/openai.ToolCall' - type: array - type: object - openai.ChatCompletionResponseFormat: - properties: - json_schema: - $ref: '#/definitions/openai.ChatCompletionResponseFormatJSONSchema' - type: - $ref: '#/definitions/openai.ChatCompletionResponseFormatType' - type: object - openai.ChatCompletionResponseFormatJSONSchema: - properties: - description: - type: string - name: - type: string - schema: {} - strict: - type: boolean - type: object - openai.ChatCompletionResponseFormatType: - enum: - - json_object - - json_schema - - text - type: string - x-enum-varnames: - - ChatCompletionResponseFormatTypeJSONObject - - ChatCompletionResponseFormatTypeJSONSchema - - ChatCompletionResponseFormatTypeText - openai.ChatMessageImageURL: - properties: - detail: - $ref: '#/definitions/openai.ImageURLDetail' - url: - type: string - type: object - openai.ChatMessagePart: - properties: - image_url: - $ref: '#/definitions/openai.ChatMessageImageURL' - text: - type: string - type: - $ref: '#/definitions/openai.ChatMessagePartType' - type: object - openai.ChatMessagePartType: - enum: - - text - - image_url - type: string - x-enum-varnames: - - ChatMessagePartTypeText - - ChatMessagePartTypeImageURL - openai.CompletionTokensDetails: - properties: - accepted_prediction_tokens: - type: integer - audio_tokens: - type: integer - reasoning_tokens: - type: integer - rejected_prediction_tokens: - type: integer - type: object - openai.ContentFilterResults: - properties: - hate: - $ref: '#/definitions/openai.Hate' - jailbreak: - $ref: '#/definitions/openai.JailBreak' - profanity: - $ref: '#/definitions/openai.Profanity' - self_harm: - $ref: '#/definitions/openai.SelfHarm' - sexual: - $ref: '#/definitions/openai.Sexual' - violence: - $ref: '#/definitions/openai.Violence' - type: object - openai.FinishReason: - enum: - - stop - - length - - function_call - - tool_calls - - content_filter - - "null" - type: string - x-enum-varnames: - - FinishReasonStop - - FinishReasonLength - - FinishReasonFunctionCall - - FinishReasonToolCalls - - FinishReasonContentFilter - - FinishReasonNull - openai.FunctionCall: - properties: - arguments: - description: call function with arguments in JSON format - type: string - name: - type: string - type: object - openai.FunctionDefinition: - properties: - description: - type: string - name: - type: string - parameters: - description: |- - Parameters is an object describing the function. - You can pass json.RawMessage to describe the schema, - or you can pass in a struct which serializes to the proper JSON schema. - The jsonschema package is provided for convenience, but you should - consider another specialized library if you require more complex schemas. - strict: - type: boolean - type: object - openai.Hate: - properties: - filtered: - type: boolean - severity: - type: string - type: object - openai.ImageURLDetail: - enum: - - high - - low - - auto - type: string - x-enum-varnames: - - ImageURLDetailHigh - - ImageURLDetailLow - - ImageURLDetailAuto - openai.JailBreak: - properties: - detected: - type: boolean - filtered: - type: boolean - type: object - openai.LogProb: - properties: - bytes: - description: Omitting the field if it is null - items: - type: integer - type: array - logprob: - type: number - token: - type: string - top_logprobs: - description: |- - TopLogProbs is a list of the most likely tokens and their log probability, at this token position. - In rare cases, there may be fewer than the number of requested top_logprobs returned. - items: - $ref: '#/definitions/openai.TopLogProbs' - type: array - type: object - openai.LogProbs: - properties: - content: - description: Content is a list of message content tokens with log probability - information. - items: - $ref: '#/definitions/openai.LogProb' - type: array - type: object - openai.Prediction: - properties: - content: - type: string - type: - type: string - type: object - openai.Profanity: - properties: - detected: - type: boolean - filtered: - type: boolean - type: object - openai.PromptFilterResult: - properties: - content_filter_results: - $ref: '#/definitions/openai.ContentFilterResults' - index: - type: integer - type: object - openai.PromptTokensDetails: - properties: - audio_tokens: - type: integer - cached_tokens: - type: integer - type: object - openai.SelfHarm: - properties: - filtered: - type: boolean - severity: - type: string - type: object - openai.ServiceTier: - enum: - - auto - - default - - flex - - priority - type: string - x-enum-varnames: - - ServiceTierAuto - - ServiceTierDefault - - ServiceTierFlex - - ServiceTierPriority - openai.Sexual: - properties: - filtered: - type: boolean - severity: - type: string - type: object - openai.StreamOptions: - properties: - include_usage: - description: |- - If set, an additional chunk will be streamed before the data: [DONE] message. - The usage field on this chunk shows the token usage statistics for the entire request, - and the choices field will always be an empty array. - All other chunks will also include a usage field, but with a null value. - type: boolean - type: object - openai.Tool: - properties: - function: - $ref: '#/definitions/openai.FunctionDefinition' - type: - $ref: '#/definitions/openai.ToolType' - type: object - openai.ToolCall: - properties: - function: - $ref: '#/definitions/openai.FunctionCall' - id: - type: string - index: - description: Index is not nil only in chat completion chunk object - type: integer - type: - $ref: '#/definitions/openai.ToolType' - type: object - openai.ToolType: - enum: - - function - type: string - x-enum-varnames: - - ToolTypeFunction - openai.TopLogProbs: - properties: - bytes: - items: - type: integer - type: array - logprob: - type: number - token: - type: string - type: object - openai.Usage: - properties: - completion_tokens: - type: integer - completion_tokens_details: - $ref: '#/definitions/openai.CompletionTokensDetails' - prompt_tokens: - type: integer - prompt_tokens_details: - $ref: '#/definitions/openai.PromptTokensDetails' - total_tokens: - type: integer - type: object - openai.Violence: - properties: - filtered: - type: boolean - severity: - type: string - type: object - projectreq.CreateProjectRequest: - properties: - instruction: - type: string - name: - type: string - required: - - name - type: object - projectreq.UpdateProjectRequest: - properties: - instruction: - type: string - is_archived: - type: boolean - is_favorite: - type: boolean - name: - type: string - type: object - projectres.ProjectDeletedResponse: - properties: - deleted: - type: boolean - id: - type: string - object: - type: string - type: object - projectres.ProjectListResponse: - properties: - data: - items: - $ref: '#/definitions/projectres.ProjectResponse' - type: array - first_id: - type: string - has_more: - type: boolean - last_id: - type: string - next_cursor: - type: string - object: - type: string - total: - type: integer - type: object - projectres.ProjectResponse: - properties: - archived_at: - type: integer - created_at: - type: integer - id: - type: string - instruction: - type: string - is_archived: - type: boolean - is_favorite: - type: boolean - name: - type: string - object: - type: string - updated_at: - type: integer - type: object - prompttemplate.CreatePromptTemplateRequest: - properties: - category: - maxLength: 100 - minLength: 1 - type: string - content: - minLength: 1 - type: string - description: - type: string - metadata: - additionalProperties: {} - type: object - name: - maxLength: 255 - minLength: 1 - type: string - template_key: - maxLength: 100 - minLength: 1 - type: string - variables: - items: - type: string - type: array - required: - - category - - content - - name - - template_key - type: object - prompttemplate.UpdatePromptTemplateRequest: - properties: - category: - maxLength: 100 - minLength: 1 - type: string - content: - minLength: 1 - type: string - description: - type: string - is_active: - type: boolean - metadata: - additionalProperties: {} - type: object - name: - maxLength: 255 - minLength: 1 - type: string - variables: - items: - type: string - type: array - type: object - prompttemplatehandler.DuplicateRequest: - properties: - new_name: - maxLength: 200 - type: string - type: object - prompttemplatehandler.ListResponse: - properties: - data: - items: - $ref: '#/definitions/prompttemplatehandler.PromptTemplateResponse' - type: array - total: - type: integer - type: object - prompttemplatehandler.PromptTemplateResponse: - properties: - category: - type: string - content: - type: string - created_at: - type: string - description: - type: string - id: - type: string - is_active: - type: boolean - is_system: - type: boolean - metadata: - additionalProperties: {} - type: object - name: - type: string - public_id: - type: string - template_key: - type: string - updated_at: - type: string - variables: - items: - type: string - type: array - version: - type: integer - type: object - requestmodels.AddProviderRequest: - properties: - active: - type: boolean - api_key: - type: string - base_url: - type: string - category: - description: '"llm" or "image", defaults to "llm"' - type: string - default_provider_image_edit: - type: boolean - default_provider_image_generate: - type: boolean - endpoints: - items: - $ref: '#/definitions/requestmodels.EndpointDTO' - type: array - metadata: - additionalProperties: - type: string - type: object - name: - type: string - url: - type: string - vendor: - type: string - required: - - name - - vendor - type: object - requestmodels.BulkEnableModelsRequest: - properties: - enable: - description: 'Required: true to enable, false to disable' - type: boolean - except_models: - description: List of model keys to exclude - items: - type: string - type: array - provider_id: - description: 'Optional: filter by provider' - minLength: 1 - type: string - required: - - enable - type: object - requestmodels.BulkToggleCatalogsRequest: - properties: - catalog_ids: - description: 'Optional: specific catalog public IDs. If empty, applies to - all catalogs' - items: - type: string - type: array - enable: - description: 'Required: true to enable, false to disable' - type: boolean - except_models: - description: List of model keys to exclude from the operation - items: - type: string - type: array - required: - - enable - type: object - requestmodels.EndpointDTO: - properties: - priority: - type: integer - url: - type: string - weight: - type: integer - type: object - requestmodels.UpdateModelCatalogRequest: - properties: - active: - type: boolean - architecture: - $ref: '#/definitions/model.Architecture' - context_length: - type: number - description: - type: string - experimental: - type: boolean - extras: - additionalProperties: {} - type: object - family: - type: string - is_moderated: - type: boolean - model_display_name: - type: string - notes: - type: string - requires_feature_flag: - type: string - supported_parameters: - $ref: '#/definitions/model.SupportedParameters' - supports_audio: - type: boolean - supports_browser: - type: boolean - supports_embeddings: - type: boolean - supports_images: - type: boolean - supports_instruct: - type: boolean - supports_reasoning: - type: boolean - supports_tools: - type: boolean - supports_video: - type: boolean - tags: - items: - type: string - type: array - type: object - requestmodels.UpdateProviderModelRequest: - properties: - active: - type: boolean - category: - type: string - category_order_number: - type: integer - family: - type: string - instruct_model_public_id: - description: Public ID of the instruct model to use when enable_thinking=false - type: string - model_display_name: - type: string - model_order_number: - type: integer - pricing: - $ref: '#/definitions/model.Pricing' - supports_audio: - type: boolean - supports_embeddings: - type: boolean - supports_images: - type: boolean - supports_reasoning: - type: boolean - supports_video: - type: boolean - token_limits: - $ref: '#/definitions/model.TokenLimits' - type: object - requestmodels.UpdateProviderRequest: - properties: - active: - type: boolean - api_key: - type: string - base_url: - type: string - default_provider_image_edit: - type: boolean - default_provider_image_generate: - type: boolean - endpoints: - items: - $ref: '#/definitions/requestmodels.EndpointDTO' - type: array - metadata: - additionalProperties: - type: string - type: object - name: - type: string - url: - type: string - type: object - responses.ErrorResponse: - properties: - code: - description: UUID from PlatformError - type: string - error: - type: string - message: - type: string - request_id: - type: string - type: object - sharerequests.CreateShareRequest: - properties: - branch: - description: Branch to share from (defaults to active branch) - type: string - include_context_messages: - description: For single-message share - type: boolean - include_images: - type: boolean - item_id: - description: Required if scope is "item" - type: string - scope: - description: '"conversation" or "item"' - enum: - - conversation - - item - type: string - title: - type: string - required: - - scope - type: object - shareresponses.AnnotationResp: - properties: - end_index: - type: integer - file_id: - type: string - start_index: - type: integer - text: - type: string - type: - type: string - url: - type: string - type: object - shareresponses.FileRefResp: - properties: - file_id: - type: string - mime_type: - type: string - name: - type: string - url: - description: For data URLs or external image URLs - type: string - type: object - shareresponses.ImageRefResp: - properties: - detail: - type: string - file_id: - type: string - url: - type: string - type: object - shareresponses.PublicShareResponse: - properties: - created_at: - type: integer - object: - type: string - slug: - type: string - snapshot: - $ref: '#/definitions/shareresponses.SnapshotResp' - title: - type: string - type: object - shareresponses.ShareDeletedResponse: - properties: - deleted: - type: boolean - id: - type: string - object: - type: string - type: object - shareresponses.ShareListResponse: - properties: - data: - items: - $ref: '#/definitions/shareresponses.ShareResponse' - type: array - object: - type: string - type: object - shareresponses.ShareOptionsResp: - properties: - include_context_messages: - type: boolean - include_images: - type: boolean - type: object - shareresponses.ShareResponse: - properties: - created_at: - type: integer - id: - type: string - item_id: - type: string - last_viewed_at: - type: integer - object: - type: string - revoked_at: - type: integer - share_options: - $ref: '#/definitions/shareresponses.ShareOptionsResp' - share_url: - type: string - slug: - type: string - snapshot_version: - type: integer - title: - type: string - updated_at: - type: integer - view_count: - type: integer - visibility: - type: string - type: object - shareresponses.SnapshotContentResp: - properties: - annotations: - items: - $ref: '#/definitions/shareresponses.AnnotationResp' - type: array - file_ref: - $ref: '#/definitions/shareresponses.FileRefResp' - image: - $ref: '#/definitions/shareresponses.ImageRefResp' - input_text: - type: string - output_text: - type: string - text: - type: string - type: - type: string - type: object - shareresponses.SnapshotItemResp: - properties: - content: - items: - $ref: '#/definitions/shareresponses.SnapshotContentResp' - type: array - created_at: - type: integer - id: - type: string - role: - type: string - type: - type: string - type: object - shareresponses.SnapshotResp: - properties: - assistant_name: - type: string - created_at: - type: integer - items: - items: - $ref: '#/definitions/shareresponses.SnapshotItemResp' - type: array - model_name: - type: string - title: - type: string - type: object - tokenusage.DailyAggregate: - properties: - date: - type: string - estimated_cost_usd: - type: number - request_count: - type: integer - total_completion_tokens: - type: integer - total_prompt_tokens: - type: integer - total_tokens: - type: integer - type: object - tokenusage.Period: - properties: - end_date: - type: string - start_date: - type: string - type: object - tokenusage.PlatformUsageResponse: - properties: - by_model: - items: - $ref: '#/definitions/tokenusage.UsageSummary' - type: array - by_provider: - items: - $ref: '#/definitions/tokenusage.UsageSummary' - type: array - period: - $ref: '#/definitions/tokenusage.Period' - top_users: - items: - $ref: '#/definitions/tokenusage.UserUsage' - type: array - total_usage: - $ref: '#/definitions/tokenusage.UsageSummary' - type: object - tokenusage.UsageResponse: - properties: - by_model: - items: - $ref: '#/definitions/tokenusage.UsageSummary' - type: array - by_provider: - items: - $ref: '#/definitions/tokenusage.UsageSummary' - type: array - period: - $ref: '#/definitions/tokenusage.Period' - total_usage: - $ref: '#/definitions/tokenusage.UsageSummary' - type: object - tokenusage.UsageSummary: - properties: - estimated_cost_usd: - type: number - model: - type: string - provider: - type: string - request_count: - type: integer - total_completion_tokens: - type: integer - total_prompt_tokens: - type: integer - total_tokens: - type: integer - type: object - tokenusage.UserUsage: - properties: - estimated_cost_usd: - type: number - request_count: - type: integer - total_completion_tokens: - type: integer - total_prompt_tokens: - type: integer - total_tokens: - type: integer - user_id: - type: string - type: object - usersettings.AdvancedSettings: - properties: - code_enabled: - description: Enable code execution features - type: boolean - web_search: - description: Let Jan automatically search the web for answers - type: boolean - type: object - usersettings.BaseStyle: - enum: - - Concise - - Friendly - - Professional - type: string - x-enum-varnames: - - BaseStyleConcise - - BaseStyleFriendly - - BaseStyleProfessional - usersettings.MemoryConfig: - properties: - enabled: - type: boolean - inject_episodic: - type: boolean - inject_semantic: - type: boolean - inject_user_core: - type: boolean - max_episodic_items: - type: integer - max_project_items: - type: integer - max_user_items: - type: integer - min_similarity: - type: number - observe_enabled: - type: boolean - type: object - usersettings.ProfileSettings: - properties: - base_style: - allOf: - - $ref: '#/definitions/usersettings.BaseStyle' - description: 'Conversation style: Concise, Friendly, or Professional' - custom_instructions: - description: Additional behavior, style, and tone preferences - type: string - more_about_you: - description: Additional information about the user - type: string - nick_name: - description: 'What should Jan call you? (alias: nickname)' - type: string - occupation: - description: User's occupation - type: string - type: object - usersettings.UpdateRequest: - properties: - advanced_settings: - $ref: '#/definitions/usersettings.AdvancedSettings' - enable_tools: - type: boolean - enable_trace: - type: boolean - memory_config: - $ref: '#/definitions/usersettings.MemoryConfig' - preferences: - additionalProperties: true - type: object - profile_settings: - $ref: '#/definitions/usersettings.ProfileSettings' - type: object - usersettingshandler.PreferencesResponse: - properties: - preferences: - additionalProperties: true - type: object - type: object - usersettingshandler.ServerCapabilities: - properties: - image_generation_enabled: - type: boolean - type: object - usersettingshandler.UpdatePreferencesRequest: - properties: - preferences: - additionalProperties: true - type: object - type: object - usersettingshandler.UserSettingsResponse: - properties: - advanced_settings: - $ref: '#/definitions/usersettings.AdvancedSettings' - created_at: - type: string - enable_tools: - type: boolean - enable_trace: - type: boolean - id: - type: integer - memory_config: - $ref: '#/definitions/usersettings.MemoryConfig' - preferences: - additionalProperties: true - type: object - profile_settings: - $ref: '#/definitions/usersettings.ProfileSettings' - server_capabilities: - $ref: '#/definitions/usersettingshandler.ServerCapabilities' - updated_at: - type: string - user_id: - type: integer - type: object -info: - contact: - name: Jan Server Team - url: https://github.com/janhq/jan-server - description: OpenAI-compatible LLM API platform with enterprise authentication, - conversation management, and streaming support. - title: Jan Server LLM API - version: "2.0" -paths: - /auth/api-keys: - get: - consumes: - - application/json - description: Returns all API keys created by the authenticated user. Key values - are not returned, only metadata. - produces: - - application/json - responses: - "200": - description: List of API keys with metadata - schema: - type: object - "401": - description: Unauthorized - invalid or expired token - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: List user's API keys - tags: - - Authentication API - post: - consumes: - - application/json - description: Creates a new API key for the authenticated user. API keys provide - programmatic access without requiring user credentials. - parameters: - - description: API key creation request with name and optional scopes - in: body - name: request - required: true - schema: - type: object - produces: - - application/json - responses: - "201": - description: API key created successfully with key value - schema: - type: object - "400": - description: Invalid request - missing required fields - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - invalid or expired token - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Create API key - tags: - - Authentication API - /auth/api-keys/{id}: - delete: - consumes: - - application/json - description: Revokes and deletes an API key by ID. Deleted keys can no longer - be used for authentication. - parameters: - - description: API key ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "204": - description: API key deleted successfully - "401": - description: Unauthorized - invalid or expired token - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: API key not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Delete API key - tags: - - Authentication API - /auth/callback: - get: - consumes: - - application/json - description: Handles the OAuth2 callback from Keycloak, exchanges authorization - code for JWT tokens - parameters: - - description: Authorization code from Keycloak - in: query - name: code - required: true - type: string - - description: State parameter for CSRF protection - in: query - name: state - required: true - type: string - produces: - - application/json - responses: - "200": - description: JWT tokens - schema: - properties: - access_token: - type: string - expires_in: - type: integer - refresh_token: - type: string - token_type: - type: string - type: object - "400": - description: Missing code or state - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Invalid state parameter - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Failed to exchange code for tokens - schema: - $ref: '#/definitions/responses.ErrorResponse' - summary: Handle Keycloak OAuth2 callback - tags: - - Authentication API - /auth/guest-login: - post: - consumes: - - application/json - description: Creates a temporary guest user account and returns JWT tokens. - Guest users have limited access and can be upgraded to full accounts later. - produces: - - application/json - responses: - "200": - description: Guest user created with access and refresh tokens - schema: - type: object - "500": - description: Internal server error - failed to create guest user - schema: - $ref: '#/definitions/responses.ErrorResponse' - summary: Create guest user account - tags: - - Authentication API - /auth/keycloak/callback: - get: - consumes: - - application/json - description: Handles the OAuth2 callback from Keycloak, exchanges authorization - code for tokens using PKCE - parameters: - - description: Authorization code from Keycloak - in: query - name: code - required: true - type: string - - description: State parameter for CSRF protection - in: query - name: state - required: true - type: string - - description: Frontend URL to redirect after successful authentication - in: query - name: redirect_url - type: string - - description: Error from Keycloak (if authentication failed) - in: query - name: error - type: string - - description: Error description from Keycloak - in: query - name: error_description - type: string - produces: - - application/json - responses: - "302": - description: Redirects to frontend URL with tokens in URL fragment - "400": - description: Missing code or state, or Keycloak error - schema: - properties: - error: - type: string - type: object - "401": - description: Invalid state parameter - schema: - properties: - error: - type: string - type: object - "500": - description: Failed to exchange code for tokens - schema: - properties: - error: - type: string - type: object - summary: Handle Keycloak OAuth2 callback - tags: - - Authentication API - /auth/keycloak/login: - get: - consumes: - - application/json - description: Redirects the user to Keycloak's authorization endpoint to authenticate. - Returns the authorization URL for frontend redirection with PKCE. - parameters: - - description: URL to redirect after successful login - in: query - name: redirect_url - type: string - produces: - - application/json - responses: - "200": - description: Authorization URL and state parameter - schema: - properties: - authorization_url: - type: string - state: - type: string - type: object - "500": - description: Failed to generate state or PKCE parameters - schema: - properties: - error: - type: string - type: object - summary: Initiate Keycloak OAuth2 login - tags: - - Authentication API - /auth/login: - get: - consumes: - - application/json - description: Returns the Keycloak authorization URL for frontend to redirect - users. Supports OAuth2 authorization code flow with PKCE. - parameters: - - description: URL to redirect after successful login - in: query - name: redirect_url - type: string - produces: - - application/json - responses: - "200": - description: Authorization URL and state parameter - schema: - properties: - authorization_url: - type: string - state: - type: string - type: object - "500": - description: Failed to initiate login - schema: - $ref: '#/definitions/responses.ErrorResponse' - summary: Initiate Keycloak OAuth2 login - tags: - - Authentication API - /auth/logout: - get: - consumes: - - application/json - description: Revokes the current access token and clears authentication cookies. - After logout, the user must re-authenticate. - produces: - - application/json - responses: - "200": - description: Successfully logged out - schema: - type: object - "401": - description: Unauthorized - invalid token - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Logout user - tags: - - Authentication API - /auth/me: - get: - consumes: - - application/json - description: Returns the authenticated user's profile information including - user ID, email, roles, and guest status. - produces: - - application/json - responses: - "200": - description: User profile information - schema: - type: object - "401": - description: Unauthorized - invalid or expired token - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Get current user information - tags: - - Authentication API - /auth/refresh-token: - post: - consumes: - - application/json - description: Exchanges a valid refresh token for a new access token. Refresh - token must be provided in Authorization header or refresh_token cookie. - parameters: - - description: Refresh token (can also be in Authorization header) - in: body - name: refresh_token - schema: - type: string - produces: - - application/json - responses: - "200": - description: New access token and refresh token - schema: - type: object - "401": - description: Unauthorized - invalid or expired refresh token - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - summary: Refresh access token - tags: - - Authentication API - /auth/revoke: - post: - consumes: - - application/json - description: Revokes a refresh token to invalidate it - parameters: - - description: Token to revoke - in: body - name: request - required: true - schema: - properties: - refresh_token: - type: string - type: object - produces: - - application/json - responses: - "200": - description: Token revoked successfully - schema: - properties: - message: - type: string - type: object - "400": - description: Invalid request body - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Keycloak OAuth is not configured - schema: - $ref: '#/definitions/responses.ErrorResponse' - summary: Revoke Keycloak refresh token - tags: - - Authentication API - /auth/upgrade: - post: - consumes: - - application/json - description: Converts a guest user account to a permanent account with email/password - credentials. Guest flag is removed and user gains full access. - parameters: - - description: Upgrade request with email and password - in: body - name: request - required: true - schema: - type: object - produces: - - application/json - responses: - "200": - description: Account upgraded successfully with new tokens - schema: - type: object - "400": - description: Invalid request - missing email or password - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - not a guest user or invalid token - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Upgrade guest to permanent account - tags: - - Authentication API - /auth/validate: - post: - consumes: - - application/json - description: Validates an access token against Keycloak's userinfo endpoint - parameters: - - description: Bearer token - in: header - name: Authorization - required: true - type: string - produces: - - application/json - responses: - "200": - description: Token is valid with user information - schema: - properties: - user_info: - type: object - valid: - type: boolean - type: object - "401": - description: Invalid or expired token - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Keycloak OAuth is not configured - schema: - $ref: '#/definitions/responses.ErrorResponse' - summary: Validate Keycloak access token - tags: - - Authentication API - /auth/validate-api-key: - post: - consumes: - - application/json - description: Internal endpoint used by Kong API Gateway to validate API keys. - Not intended for direct client use. - parameters: - - description: API key validation request - in: body - name: request - required: true - schema: - type: object - produces: - - application/json - responses: - "200": - description: API key is valid with user information - schema: - type: object - "401": - description: Invalid API key - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - summary: Validate API key (Kong Plugin) - tags: - - Authentication API - /v1/admin/mcp-tools: - get: - consumes: - - application/json - description: Get a paginated list of MCP tools with optional filtering - parameters: - - description: Filter by category - in: query - name: category - type: string - - description: Filter by active status - in: query - name: is_active - type: boolean - - description: Search in name, description, and tool_key - in: query - name: search - type: string - - default: 20 - description: Limit - in: query - name: limit - type: integer - - default: 0 - description: Offset - in: query - name: offset - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/mcptoolhandler.ListResponse' - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: List MCP tools - tags: - - Admin - MCP Tools - /v1/admin/mcp-tools/{id}: - get: - consumes: - - application/json - description: Get an MCP tool by public ID - parameters: - - description: Public ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/mcptoolhandler.MCPToolResponse' - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: Get an MCP tool - tags: - - Admin - MCP Tools - patch: - consumes: - - application/json - description: Update an existing MCP tool configuration (Name is read-only) - parameters: - - description: Public ID - in: path - name: id - required: true - type: string - - description: Request body - in: body - name: body - required: true - schema: - $ref: '#/definitions/mcptool.UpdateMCPToolRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/mcptoolhandler.MCPToolResponse' - "400": - description: Bad Request - schema: - additionalProperties: - type: string - type: object - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: Update an MCP tool - tags: - - Admin - MCP Tools - /v1/admin/models/catalogs: - get: - description: Retrieves a paginated list of model catalogs with optional filtering - and searching - parameters: - - description: 'Number of records to return (default: 20, max: 100)' - in: query - name: limit - type: integer - - description: Number of records to skip for pagination - in: query - name: offset - type: integer - - description: 'Sort order: asc or desc (default: desc)' - in: query - name: order - type: string - - description: 'Filter by status: init, filled, updated' - in: query - name: status - type: string - - description: Filter by moderation status - in: query - name: is_moderated - type: boolean - produces: - - application/json - responses: - "200": - description: List of model catalogs - schema: - $ref: '#/definitions/modelresponses.ModelCatalogResponse' - "400": - description: Invalid query parameters - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: List all model catalogs - tags: - - Admin Model API - /v1/admin/models/catalogs/{model_public_id}: - get: - description: Retrieves detailed information about a model catalog entry by its - public ID (supports IDs with slashes) - parameters: - - description: Model Catalog Public ID (can contain slashes) - in: path - name: model_public_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Model catalog details - schema: - $ref: '#/definitions/modelresponses.ModelCatalogResponse' - "400": - description: Invalid request - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Model catalog not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Get a model catalog entry - tags: - - Admin Model API - patch: - consumes: - - application/json - description: Updates metadata for a model catalog entry. Marks it as manually - updated to prevent auto-sync overwrites. - parameters: - - description: Model Catalog Public ID (can contain slashes) - in: path - name: model_public_id - required: true - type: string - - description: Update payload - in: body - name: payload - required: true - schema: - $ref: '#/definitions/requestmodels.UpdateModelCatalogRequest' - produces: - - application/json - responses: - "200": - description: Updated model catalog - schema: - $ref: '#/definitions/modelresponses.ModelCatalogResponse' - "400": - description: Invalid request payload - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Model catalog not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Update a model catalog entry - tags: - - Admin Model API - /v1/admin/models/catalogs/bulk-toggle: - post: - consumes: - - application/json - description: Enable or disable provider models for specific catalogs or ALL - catalogs, with optional exception list. Supports "enable/disable all except" - patterns globally or scoped to catalogs. - parameters: - - description: Bulk toggle request. If catalog_ids is empty, applies to ALL - catalogs. Use except_models to exclude specific models. - in: body - name: request - required: true - schema: - $ref: '#/definitions/requestmodels.BulkToggleCatalogsRequest' - produces: - - application/json - responses: - "200": - description: Bulk operation result with counts and status - schema: - $ref: '#/definitions/modelresponses.BulkOperationResponse' - "400": - description: Invalid request - exceeds limits or validation error - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: One or more catalog IDs not found (when catalog_ids provided) - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error during bulk operation - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Bulk enable/disable provider models by catalog IDs or all catalogs - tags: - - Admin Model API - /v1/admin/models/prompt-templates/assign/{model_id}: - post: - consumes: - - application/json - description: Assign or update a prompt template assignment for a model catalog - parameters: - - description: Model Catalog Public ID - in: path - name: model_id - required: true - type: string - - description: Assignment request - in: body - name: body - required: true - schema: - $ref: '#/definitions/modelprompthandler.AssignRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/modelprompthandler.ModelPromptTemplateResponse' - "201": - description: Created - schema: - $ref: '#/definitions/modelprompthandler.ModelPromptTemplateResponse' - "400": - description: Bad Request - schema: - additionalProperties: - type: string - type: object - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: Assign a prompt template to a model - tags: - - Admin - Model Prompt Templates - /v1/admin/models/prompt-templates/effective/{model_id}: - get: - consumes: - - application/json - description: Get all resolved templates for a model, including global defaults - parameters: - - description: Model Catalog Public ID - in: path - name: model_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/modelprompthandler.EffectiveTemplatesResponse' - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: Get effective templates for a model - tags: - - Admin - Model Prompt Templates - /v1/admin/models/prompt-templates/list/{model_id}: - get: - consumes: - - application/json - description: Get all prompt template assignments for a model catalog - parameters: - - description: Model Catalog Public ID - in: path - name: model_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/modelprompthandler.ListResponse' - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: List model prompt template assignments - tags: - - Admin - Model Prompt Templates - /v1/admin/models/prompt-templates/unassign/{template_key}/{model_id}: - delete: - consumes: - - application/json - description: Remove a prompt template assignment, reverting to global default - parameters: - - description: Model Catalog Public ID - in: path - name: model_id - required: true - type: string - - description: Template Key (e.g., deep_research, timing) - in: path - name: template_key - required: true - type: string - produces: - - application/json - responses: - "204": - description: No Content - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: Remove a prompt template assignment from a model - tags: - - Admin - Model Prompt Templates - /v1/admin/models/prompt-templates/update/{template_key}/{model_id}: - patch: - consumes: - - application/json - description: Update an existing prompt template assignment - parameters: - - description: Model Catalog Public ID - in: path - name: model_id - required: true - type: string - - description: Template Key - in: path - name: template_key - required: true - type: string - - description: Update request - in: body - name: body - required: true - schema: - $ref: '#/definitions/modelprompthandler.UpdateRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/modelprompthandler.ModelPromptTemplateResponse' - "400": - description: Bad Request - schema: - additionalProperties: - type: string - type: object - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: Update a prompt template assignment - tags: - - Admin - Model Prompt Templates - /v1/admin/models/provider-models: - get: - description: Retrieves a paginated list of provider models with optional filtering - parameters: - - description: 'Number of records to return (default: 20, max: 100)' - in: query - name: limit - type: integer - - description: Number of records to skip for pagination - in: query - name: offset - type: integer - - description: 'Sort order: asc or desc (default: desc)' - in: query - name: order - type: string - - description: Filter by provider public ID - in: query - name: provider_id - type: string - - description: Filter by model key - in: query - name: model_key - type: string - - description: Filter by active status - in: query - name: active - type: boolean - - description: Filter by image support - in: query - name: supports_images - type: boolean - produces: - - application/json - responses: - "200": - description: List of provider models - schema: - $ref: '#/definitions/modelresponses.ProviderModelResponse' - "400": - description: Invalid query parameters - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: List all provider models - tags: - - Admin Model API - /v1/admin/models/provider-models/{provider_model_public_id}: - get: - description: Retrieves detailed information about a provider model by its public - ID - parameters: - - description: Provider Model Public ID - in: path - name: provider_model_public_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Provider model details - schema: - $ref: '#/definitions/modelresponses.ProviderModelResponse' - "400": - description: Invalid request - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Provider model not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Get a provider model - tags: - - Admin Model API - patch: - consumes: - - application/json - description: Updates configuration for a provider model including pricing, limits, - and feature flags - parameters: - - description: Provider Model Public ID - in: path - name: provider_model_public_id - required: true - type: string - - description: Update payload - in: body - name: payload - required: true - schema: - $ref: '#/definitions/requestmodels.UpdateProviderModelRequest' - produces: - - application/json - responses: - "200": - description: Updated provider model - schema: - $ref: '#/definitions/modelresponses.ProviderModelResponse' - "400": - description: Invalid request payload - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Provider model not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Update a provider model - tags: - - Admin Model API - /v1/admin/models/provider-models/bulk-toggle: - post: - consumes: - - application/json - description: 'Enables or disables provider models with flexible patterns: enable - all, disable all, enable all except, or disable all except. Optionally filter - by provider.' - parameters: - - description: Bulk toggle payload with enable flag, optional provider filter, - and exception list - in: body - name: payload - required: true - schema: - $ref: '#/definitions/requestmodels.BulkEnableModelsRequest' - produces: - - application/json - responses: - "200": - description: Bulk operation result with counts and status - schema: - $ref: '#/definitions/modelresponses.BulkOperationResponse' - "400": - description: Invalid request payload - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Bulk enable or disable provider models - tags: - - Admin Model API - /v1/admin/prompt-templates: - get: - consumes: - - application/json - description: Get a paginated list of prompt templates with optional filtering - parameters: - - description: Filter by category - in: query - name: category - type: string - - description: Filter by active status - in: query - name: is_active - type: boolean - - description: Filter by system status - in: query - name: is_system - type: boolean - - description: Search in name and description - in: query - name: search - type: string - - default: 20 - description: Limit - in: query - name: limit - type: integer - - default: 0 - description: Offset - in: query - name: offset - type: integer - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/prompttemplatehandler.ListResponse' - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: List prompt templates - tags: - - Admin - Prompt Templates - post: - consumes: - - application/json - description: Create a new prompt template - parameters: - - description: Request body - in: body - name: body - required: true - schema: - $ref: '#/definitions/prompttemplate.CreatePromptTemplateRequest' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/prompttemplatehandler.PromptTemplateResponse' - "400": - description: Bad Request - schema: - additionalProperties: - type: string - type: object - "409": - description: Conflict - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: Create a prompt template - tags: - - Admin - Prompt Templates - /v1/admin/prompt-templates/{id}: - delete: - consumes: - - application/json - description: Delete a prompt template (system templates cannot be deleted) - parameters: - - description: Public ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "204": - description: No Content - "403": - description: Forbidden - schema: - additionalProperties: - type: string - type: object - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: Delete a prompt template - tags: - - Admin - Prompt Templates - get: - consumes: - - application/json - description: Get a prompt template by public ID - parameters: - - description: Public ID - in: path - name: id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/prompttemplatehandler.PromptTemplateResponse' - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: Get a prompt template - tags: - - Admin - Prompt Templates - patch: - consumes: - - application/json - description: Update an existing prompt template - parameters: - - description: Public ID - in: path - name: id - required: true - type: string - - description: Request body - in: body - name: body - required: true - schema: - $ref: '#/definitions/prompttemplate.UpdatePromptTemplateRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/prompttemplatehandler.PromptTemplateResponse' - "400": - description: Bad Request - schema: - additionalProperties: - type: string - type: object - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: Update a prompt template - tags: - - Admin - Prompt Templates - /v1/admin/prompt-templates/{id}/duplicate: - post: - consumes: - - application/json - description: Create a copy of an existing prompt template with a new name (key - is auto-generated) - parameters: - - description: Public ID - in: path - name: id - required: true - type: string - - description: Request body - in: body - name: body - required: true - schema: - $ref: '#/definitions/prompttemplatehandler.DuplicateRequest' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/prompttemplatehandler.PromptTemplateResponse' - "400": - description: Bad Request - schema: - additionalProperties: - type: string - type: object - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "409": - description: Conflict - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: Duplicate a prompt template - tags: - - Admin - Prompt Templates - /v1/admin/providers: - get: - description: Retrieves all providers with their model counts - produces: - - application/json - responses: - "200": - description: List of providers with model counts - schema: - items: - $ref: '#/definitions/modelresponses.ProviderWithModelCountResponse' - type: array - "500": - description: Failed to retrieve providers - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Get all providers - tags: - - Admin Provider API - post: - consumes: - - application/json - description: Registers a new provider and synchronizes its available models. - parameters: - - description: Provider registration payload - in: body - name: payload - required: true - schema: - $ref: '#/definitions/requestmodels.AddProviderRequest' - produces: - - application/json - responses: - "200": - description: Registered provider with synced models - schema: - $ref: '#/definitions/modelresponses.ProviderWithModelsResponse' - "400": - description: Invalid request payload - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Failed to register provider - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Register a provider - tags: - - Admin Provider API - /v1/admin/providers/{provider_public_id}: - delete: - description: Deletes a provider by its public ID along with its provider models - parameters: - - description: Provider public ID - in: path - name: provider_public_id - required: true - type: string - produces: - - application/json - responses: - "204": - description: Provider deleted - "404": - description: Provider not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Failed to delete provider - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Delete a provider - tags: - - Admin Provider API - get: - description: Retrieves a provider by its public ID - parameters: - - description: Provider public ID - in: path - name: provider_public_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Provider details - schema: - $ref: '#/definitions/modelresponses.ProviderResponse' - "404": - description: Provider not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Failed to retrieve provider - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Get a provider - tags: - - Admin Provider API - patch: - consumes: - - application/json - description: Updates an existing provider's configuration - parameters: - - description: Provider public ID - in: path - name: provider_public_id - required: true - type: string - - description: Provider update payload - in: body - name: payload - required: true - schema: - $ref: '#/definitions/requestmodels.UpdateProviderRequest' - produces: - - application/json - responses: - "200": - description: Updated provider - schema: - $ref: '#/definitions/modelresponses.ProviderResponse' - "400": - description: Invalid request payload - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Provider not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Failed to update provider - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Update a provider - tags: - - Admin Provider API - /v1/admin/usage: - get: - description: Returns total platform token usage including top users and breakdown - by model/provider - parameters: - - description: Start date (YYYY-MM-DD), defaults to 30 days ago - in: query - name: start_date - type: string - - description: End date (YYYY-MM-DD), defaults to today - in: query - name: end_date - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/tokenusage.PlatformUsageResponse' - "401": - description: Unauthorized - schema: - additionalProperties: - type: string - type: object - "403": - description: Forbidden - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - security: - - BearerAuth: [] - summary: Get platform-wide token usage (Admin only) - tags: - - Usage - /v1/auth/logout: - get: - consumes: - - application/json - description: Remove refresh tokens to perform logout and invalidate Keycloak - session. Accepts refresh token from cookie, Authorization header, or request - body. - parameters: - - description: Refresh token to revoke - in: body - name: refresh_token - schema: - type: string - - description: Bearer refresh_token - in: header - name: Authorization - type: string - produces: - - application/json - responses: - "200": - description: Successfully logged out - schema: - additionalProperties: - type: string - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/responses.ErrorResponse' - summary: Logout - tags: - - Authentication API - post: - consumes: - - application/json - description: Remove refresh tokens to perform logout and invalidate Keycloak - session. Accepts refresh token from cookie, Authorization header, or request - body. - parameters: - - description: Refresh token to revoke - in: body - name: refresh_token - schema: - type: string - - description: Bearer refresh_token - in: header - name: Authorization - type: string - produces: - - application/json - responses: - "200": - description: Successfully logged out - schema: - additionalProperties: - type: string - type: object - "400": - description: Bad Request - schema: - $ref: '#/definitions/responses.ErrorResponse' - summary: Logout - tags: - - Authentication API - /v1/auth/me: - get: - description: Retrieves the profile of the authenticated user - produces: - - application/json - responses: - "200": - description: Successfully retrieved user profile - schema: - $ref: '#/definitions/authhandler.GetMeResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Get user profile - tags: - - Authentication API - /v1/auth/refresh-token: - post: - consumes: - - application/json - description: Use a valid refresh token to obtain a new access token - parameters: - - description: Refresh token (can also be in Authorization header) - in: body - name: refresh_token - schema: - type: string - produces: - - application/json - responses: - "200": - description: Successfully refreshed the access token - schema: - $ref: '#/definitions/authhandler.AccessTokenResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - summary: Refresh an access token - tags: - - Authentication API - /v1/chat/completions: - post: - consumes: - - application/json - description: |- - Generates a model response for the given chat conversation. This is a standard chat completion API that supports both streaming and non-streaming modes without conversation persistence. - - **Streaming Mode (stream=true):** - - Returns Server-Sent Events (SSE) with real-time streaming - - Streams completion chunks directly from the inference model - - Final event contains "[DONE]" marker - - **Non-Streaming Mode (stream=false or omitted):** - - Returns single JSON response with complete completion - - Standard OpenAI ChatCompletionResponse format - - **Storage Options:** - - `store=true`: Persist the latest input message and assistant response to the active conversation - - `store_reasoning=true`: Additionally persist reasoning content provided by the model - - When `store` is omitted or false, the conversation remains read-only - - **Features:** - - Supports all OpenAI ChatCompletionRequest parameters - - Optional conversation context for conversation persistence - - User authentication required - - Direct inference model integration - parameters: - - description: Chat completion request with streaming options and optional conversation - in: body - name: request - required: true - schema: - $ref: '#/definitions/chatrequests.ChatCompletionRequest' - produces: - - application/json - - text/event-stream - responses: - "200": - description: 'Successful streaming response (when stream=true) - SSE format - with data: {json} events' - schema: - type: string - "400": - description: Invalid request payload, empty messages, or inference failure - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Create a chat completion - tags: - - Chat Completions API - /v1/conversations: - delete: - description: |- - Permanently delete all conversations for the authenticated user - - **WARNING: This is a destructive operation that cannot be undone.** - - **Features:** - - Deletes ALL conversations owned by the authenticated user - - Automatically cascades to delete all associated items and shares - - Returns the count of deleted conversations - - Requires valid authentication to ensure ownership verification - - **Security:** - - Only deletes conversations owned by the authenticated user - - Cannot delete other users' conversations - - Authentication is mandatory - produces: - - application/json - responses: - "200": - description: Successfully deleted all conversations - schema: - $ref: '#/definitions/conversationresponses.BulkConversationsDeletedResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - deletion failed - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Delete all conversations - tags: - - Conversations API - get: - description: List conversations for the authenticated user with optional referrer - filtering. - parameters: - - description: Referrer filter - in: query - name: referrer - type: string - - description: Maximum number of conversations to return - in: query - name: limit - type: integer - - description: Return conversations created after the given numeric ID - in: query - name: after - type: string - - description: Sort order (asc or desc) - in: query - name: order - type: string - - description: Set to 'all' to list conversations across the workspace (requires - elevated permissions) - in: query - name: scope - type: string - produces: - - application/json - responses: - "200": - description: Successfully retrieved conversations - schema: - $ref: '#/definitions/conversationresponses.ConversationListResponse' - "400": - description: Invalid request parameters - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: List conversations - tags: - - Conversations API - post: - consumes: - - application/json - description: |- - Create a new conversation to store and retrieve conversation state across Response API calls - - **Features:** - - Create conversation with optional metadata (max 16 key-value pairs) - - Add up to 20 initial items to the conversation - - Returns conversation ID with `conv_` prefix - - Supports OpenAI Conversations API format - - **Metadata Constraints:** - - Maximum 16 key-value pairs - - Keys: max 64 characters - - Values: max 512 characters - parameters: - - description: Create conversation request with optional items and metadata - in: body - name: request - required: true - schema: - $ref: '#/definitions/conversationrequests.CreateConversationRequest' - produces: - - application/json - responses: - "200": - description: Successfully created conversation - schema: - $ref: '#/definitions/conversationresponses.ConversationResponse' - "400": - description: Invalid request - validation failed or too many items - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - conversation creation failed - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Create a conversation - tags: - - Conversations API - /v1/conversations/{conv_public_id}: - delete: - description: |- - Delete a conversation (soft delete). Items in the conversation will not be deleted but will be inaccessible. - - **Features:** - - Soft delete (conversation marked as deleted, not physically removed) - - Items remain in database but become inaccessible - - Automatic ownership verification - - Returns deletion confirmation with conversation ID - - **Response:** - - `id`: Deleted conversation ID - - `object`: Always "conversation.deleted" - - `deleted`: Always true - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Successfully deleted conversation - schema: - $ref: '#/definitions/conversationresponses.ConversationDeletedResponse' - "400": - description: Invalid conversation ID format - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Conversation not found or access denied - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - deletion failed - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Delete a conversation - tags: - - Conversations API - get: - description: |- - Retrieve a conversation by ID with ownership verification - - **Features:** - - Retrieves conversation metadata including creation timestamp - - Automatic ownership verification (user can only access their own conversations) - - Returns OpenAI-compatible conversation object - - **Response Fields:** - - `id`: Conversation ID with `conv_` prefix - - `object`: Always "conversation" - - `created_at`: Unix timestamp - - `metadata`: User-defined key-value pairs - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Successfully retrieved conversation - schema: - $ref: '#/definitions/conversationresponses.ConversationResponse' - "400": - description: Invalid conversation ID format - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Conversation not found or access denied - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Get a conversation - tags: - - Conversations API - post: - consumes: - - application/json - description: |- - Update a conversation's metadata while preserving existing items - - **Features:** - - Update metadata key-value pairs - - Replaces entire metadata object (not merged) - - Items remain unchanged - - Automatic ownership verification - - **Metadata Constraints:** - - Maximum 16 key-value pairs - - Keys: max 64 characters - - Values: max 512 characters - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - - description: Update conversation request with new metadata - in: body - name: request - required: true - schema: - $ref: '#/definitions/conversationrequests.UpdateConversationRequest' - produces: - - application/json - responses: - "200": - description: Successfully updated conversation - schema: - $ref: '#/definitions/conversationresponses.ConversationResponse' - "400": - description: Invalid request - validation failed or invalid metadata - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Conversation not found or access denied - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - update failed - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Update a conversation - tags: - - Conversations API - /v1/conversations/{conv_public_id}/branches: - get: - description: List all branches for a conversation - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Successfully retrieved branches - schema: - $ref: '#/definitions/conversationhandler.ListBranchesResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Conversation not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: List branches - tags: - - Conversation Branches - post: - consumes: - - application/json - description: Create a new branch in a conversation, optionally forking from - an existing item - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - - description: Create branch request - in: body - name: request - required: true - schema: - $ref: '#/definitions/conversationhandler.CreateBranchRequest' - produces: - - application/json - responses: - "201": - description: Successfully created branch - schema: - $ref: '#/definitions/conversationhandler.BranchResponse' - "400": - description: Invalid request - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Conversation not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Create a branch - tags: - - Conversation Branches - /v1/conversations/{conv_public_id}/branches/{branch_name}: - delete: - description: Delete a branch from a conversation (cannot delete MAIN or active - branch) - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - - description: Branch name - in: path - name: branch_name - required: true - type: string - responses: - "204": - description: Branch deleted successfully - "400": - description: Cannot delete MAIN or active branch - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Branch not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Delete a branch - tags: - - Conversation Branches - get: - description: Get details of a specific branch - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - - description: Branch name - in: path - name: branch_name - required: true - type: string - produces: - - application/json - responses: - "200": - description: Successfully retrieved branch - schema: - $ref: '#/definitions/conversationhandler.BranchResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Branch not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Get branch details - tags: - - Conversation Branches - /v1/conversations/{conv_public_id}/branches/{branch_name}/activate: - post: - description: Set a branch as the active branch for a conversation - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - - description: Branch name - in: path - name: branch_name - required: true - type: string - produces: - - application/json - responses: - "200": - description: Branch activated successfully - schema: - $ref: '#/definitions/conversationhandler.ActivateBranchResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Branch not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Activate a branch - tags: - - Conversation Branches - /v1/conversations/{conv_public_id}/items: - get: - description: |- - List all items in a conversation with cursor-based pagination support - - **Features:** - - Cursor-based pagination using item IDs - - Configurable page size (1-100 items, default 20) - - Sort order control (ascending or descending) - - Optional include parameter for additional fields - - Returns paginated list with navigation cursors - - **Pagination:** - - Use `after` cursor from previous response for next page - - `has_more` indicates if more items are available - - `first_id` and `last_id` provide cursor references - - **Query Parameters:** - - `limit`: Number of items (1-100, default 20) - - `order`: Sort order ("asc" or "desc", default "desc") - - `after`: Item ID cursor for pagination - - `include`: Additional fields to include (optional) - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - - description: Item ID cursor to list items after (pagination) - in: query - name: after - type: string - - default: 20 - description: Number of items to return (1-100) - in: query - maximum: 100 - minimum: 1 - name: limit - type: integer - - default: desc - description: 'Sort order: asc or desc' - enum: - - asc - - desc - in: query - name: order - type: string - - collectionFormat: csv - description: Additional fields to include in response - in: query - items: - type: string - name: include - type: array - produces: - - application/json - responses: - "200": - description: Successfully retrieved items list - schema: - $ref: '#/definitions/conversationresponses.ItemListResponse' - "400": - description: Invalid request - invalid parameters or conversation ID - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Conversation not found or access denied - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - listing failed - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: List conversation items - tags: - - Conversations API - post: - consumes: - - application/json - description: |- - Add items to a conversation. You may add up to 20 items at a time. - - **Features:** - - Bulk item creation (max 20 items per request) - - Automatic item ID generation with `msg_` prefix - - Items added to conversation's active branch (default: MAIN) - - Returns list of created items with generated IDs - - **Item Types:** - - `message`: User or assistant messages - - `tool_call`: Tool/function call items - - `tool_response`: Tool/function response items - - Other OpenAI-compatible item types - - **Constraints:** - - Maximum 20 items per request - - Each item must have valid type and content - - Items are immutable after creation - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - - collectionFormat: csv - description: Additional fields to include in response - in: query - items: - type: string - name: include - type: array - - description: Create items request with array of items - in: body - name: request - required: true - schema: - $ref: '#/definitions/conversationrequests.CreateItemsRequest' - produces: - - application/json - responses: - "200": - description: Successfully created items - schema: - $ref: '#/definitions/conversationresponses.ConversationItemCreatedResponse' - "400": - description: Invalid request - too many items, invalid format, or validation - failed - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Conversation not found or access denied - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - item creation failed - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Create conversation items - tags: - - Conversations API - /v1/conversations/{conv_public_id}/items/{item_id}: - delete: - description: |- - Delete an item from a conversation by creating a new MAIN branch without it. - The old MAIN branch is preserved as a backup. - - **Features:** - - Creates a new branch without the deleted item - - New branch becomes MAIN, old MAIN becomes backup - - Automatic ownership verification - - Preserves conversation history in backup branch - - **Important:** - - The old MAIN branch is renamed to MAIN_YYYYMMDDHHMMSS - - You can switch back to the backup branch if needed - - This is a non-destructive delete operation - - **Response:** - Returns branch information including the backup branch name - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - - description: 'Item ID to delete (format: msg_xxxxx)' - in: path - name: item_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Successfully deleted item, returns branch info - schema: - $ref: '#/definitions/conversationhandler.DeleteItemResponse' - "400": - description: Invalid conversation ID or item ID format - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Conversation or item not found, or access denied - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - deletion failed - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Delete a conversation item - tags: - - Conversations API - get: - description: |- - Retrieve a single item from a conversation by item ID - - **Features:** - - Retrieve specific item by ID - - Returns complete item with all content - - Automatic ownership verification via conversation - - Optional include parameter for additional fields - - **Response Fields:** - - `id`: Item ID with `msg_` prefix - - `type`: Item type (message, tool_call, etc.) - - `role`: Role for message items (user, assistant) - - `content`: Item content array - - `status`: Item status (completed, incomplete, etc.) - - `created_at`: Unix timestamp - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - - description: 'Item ID (format: msg_xxxxx)' - in: path - name: item_id - required: true - type: string - - collectionFormat: csv - description: Additional fields to include in response - in: query - items: - type: string - name: include - type: array - produces: - - application/json - responses: - "200": - description: Successfully retrieved item - schema: - $ref: '#/definitions/conversationresponses.ItemResponse' - "400": - description: Invalid conversation ID or item ID format - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Conversation or item not found, or access denied - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Get a conversation item - tags: - - Conversations API - /v1/conversations/{conv_public_id}/items/{item_id}/edit: - post: - consumes: - - application/json - description: Edit a user message and create a new branch with the edited content - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - - description: 'Message item ID (format: msg_xxxxx)' - in: path - name: item_id - required: true - type: string - - description: Edit message request - in: body - name: request - required: true - schema: - $ref: '#/definitions/conversationhandler.EditMessageRequest' - produces: - - application/json - responses: - "200": - description: Message edited successfully - schema: - $ref: '#/definitions/conversationhandler.EditMessageResponse' - "400": - description: Invalid request or not a user message - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Message not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Edit a message - tags: - - Message Actions - /v1/conversations/{conv_public_id}/items/{item_id}/regenerate: - post: - consumes: - - application/json - description: Regenerate an assistant response by creating a new branch - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - - description: 'Assistant message item ID (format: msg_xxxxx)' - in: path - name: item_id - required: true - type: string - - description: Regenerate options - in: body - name: request - schema: - $ref: '#/definitions/conversationhandler.RegenerateMessageRequest' - produces: - - application/json - responses: - "200": - description: Regeneration initiated - schema: - $ref: '#/definitions/conversationhandler.RegenerateMessageResponse' - "400": - description: Invalid request or not an assistant message - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Message not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Regenerate a response - tags: - - Message Actions - /v1/conversations/{conv_public_id}/items/by-call-id/{call_id}: - patch: - consumes: - - application/json - description: |- - Update a conversation item's status and output using its call_id. - This endpoint is primarily used by MCP tools to report tool execution results. - - **Features:** - - Find item by call_id (e.g., call_xxx) instead of item_id - - Update status to completed, failed, or cancelled - - Store tool output or error message - - Automatic timestamp for completion - - **Use Cases:** - - MCP tool reports successful execution with output - - MCP tool reports failure with error message - - Tool call status tracking and observability - parameters: - - description: 'Conversation ID (format: conv_xxxxx)' - in: path - name: conv_public_id - required: true - type: string - - description: 'Call ID of the tool call item (format: call_xxxxx)' - in: path - name: call_id - required: true - type: string - - description: Update request with status and optional output/error - in: body - name: request - required: true - schema: - $ref: '#/definitions/conversationrequests.UpdateItemByCallIDRequest' - produces: - - application/json - responses: - "200": - description: Successfully updated item - schema: - $ref: '#/definitions/conversationresponses.ItemResponse' - "400": - description: Invalid request - validation failed - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Conversation or item not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Update item by call ID - tags: - - Conversations API - /v1/conversations/{conv_public_id}/share: - post: - consumes: - - application/json - description: Creates a public share link for a conversation or a single message - parameters: - - description: Conversation public ID - in: path - name: conv_public_id - required: true - type: string - - description: Share creation request - in: body - name: request - required: true - schema: - $ref: '#/definitions/sharerequests.CreateShareRequest' - produces: - - application/json - responses: - "201": - description: Share created successfully - schema: - $ref: '#/definitions/shareresponses.ShareResponse' - "400": - description: Invalid request - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "403": - description: Forbidden - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Conversation not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "413": - description: Snapshot too large - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Create a share for a conversation - tags: - - Shares API - /v1/conversations/{conv_public_id}/shares: - get: - description: Lists all shares (active and revoked) for a conversation - parameters: - - description: Conversation public ID - in: path - name: conv_public_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: List of shares - schema: - $ref: '#/definitions/shareresponses.ShareListResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "403": - description: Forbidden - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Conversation not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: List shares for a conversation - tags: - - Shares API - /v1/conversations/{conv_public_id}/shares/{share_id}: - delete: - description: Revokes an active share, making it inaccessible - parameters: - - description: Conversation public ID - in: path - name: conv_public_id - required: true - type: string - - description: Share public ID - in: path - name: share_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Share revoked successfully - schema: - $ref: '#/definitions/shareresponses.ShareDeletedResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "403": - description: Forbidden - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Share not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Revoke a share - tags: - - Shares API - /v1/healthz: - get: - description: Returns the health status of the API server. Used by orchestrators - and monitoring systems. - produces: - - application/json - responses: - "200": - description: Health status OK - schema: - additionalProperties: - type: string - type: object - summary: Health check endpoint - tags: - - Server API - /v1/images/edits: - post: - consumes: - - application/json - description: Creates an edited image given an original image and a prompt. - parameters: - - description: Image edit request - in: body - name: request - required: true - schema: - $ref: '#/definitions/image.ImageEditRequest' - produces: - - application/json - responses: - "200": - description: Successful image edit response - schema: - $ref: '#/definitions/image.ImageGenerationResponse' - "400": - description: Invalid request payload or validation error - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: No active image provider configured - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error or image provider error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Create image edit - tags: - - Images API - /v1/images/generations: - post: - consumes: - - application/json - description: | - Generates images from a text prompt using the configured image provider. - This endpoint is compatible with the OpenAI Images API format. - - **Response Formats:** - - url: Returns presigned URLs to download images (default, recommended) - - b64_json: Returns base64-encoded image data - - **Size Options:** - - 1024x1024 (default) - - 512x512 - - 1792x1024 (landscape) - - 1024x1792 (portrait) - parameters: - - description: Image generation request - in: body - name: request - required: true - schema: - $ref: '#/definitions/image.ImageGenerationRequest' - produces: - - application/json - responses: - "200": - description: Successful image generation response - schema: - $ref: '#/definitions/image.ImageGenerationResponse' - "400": - description: Invalid request payload or validation error - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - missing or invalid authentication - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: No active image provider configured - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error or image provider error - schema: - $ref: '#/definitions/responses.ErrorResponse' - "501": - description: Feature not implemented - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Create image generation - tags: - - Images API - /v1/images/variations: - post: - consumes: - - application/json - description: |- - Creates a variation of a given image. - NOTE: This endpoint is not yet implemented and will return 501 Not Implemented. - produces: - - application/json - responses: - "501": - description: Not implemented - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Create image variation (Not Implemented) - tags: - - Images API - /v1/mcp-tools: - get: - consumes: - - application/json - description: Get all active MCP tools (public endpoint for mcp-tools service) - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/mcptoolhandler.ActiveToolsResponse' - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: List all active MCP tools - tags: - - MCP Tools - /v1/mcp-tools/{key}: - get: - consumes: - - application/json - description: Get an MCP tool by its unique tool key (public endpoint for mcp-tools - service) - parameters: - - description: Tool Key - in: path - name: key - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/mcptoolhandler.MCPToolResponse' - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: Get an MCP tool by key - tags: - - MCP Tools - /v1/models: - get: - consumes: - - application/json - description: Retrieves a list of available models that can be used for chat - completions or other tasks. Returns either simple model list or detailed list - with provider metadata based on X-PROVIDER-DATA header. - parameters: - - description: Set to 'true' to include provider metadata in response - enum: - - "true" - - "false" - in: header - name: X-PROVIDER-DATA - type: string - produces: - - application/json - responses: - "200": - description: List of models with provider metadata (when X-PROVIDER-DATA=true) - schema: - $ref: '#/definitions/modelresponses.ModelWithProviderResponseList' - "404": - description: Models or providers not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Failed to retrieve models - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: List available models - tags: - - Chat Completions API - /v1/models/catalogs/{model_public_id}: - get: - description: Retrieves detailed information about a model catalog entry by its - public ID (supports IDs with slashes like openrouter/nova-lite-v1) - parameters: - - description: Model Catalog Public ID (can contain slashes) - in: path - name: model_public_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Model catalog details - schema: - $ref: '#/definitions/modelresponses.ModelCatalogResponse' - "400": - description: Invalid request - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Model catalog not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal server error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Get a model catalog entry - tags: - - Model API - /v1/models/providers: - get: - consumes: - - application/json - description: Retrieves a list of available model providers that can be used - for inference. - produces: - - application/json - responses: - "200": - description: List of providers - schema: - $ref: '#/definitions/modelresponses.ProviderResponseList' - "500": - description: Failed to retrieve providers - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: List model providers - tags: - - Model API - /v1/projects: - get: - description: List all projects for the authenticated user - parameters: - - description: Maximum number of projects to return - in: query - name: limit - type: integer - - description: Return projects after the given numeric ID - in: query - name: after - type: string - - description: Sort order (asc or desc) - in: query - name: order - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/projectres.ProjectListResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: List projects - tags: - - Projects API - post: - consumes: - - application/json - description: Create a new project for grouping conversations - parameters: - - description: Create project request - in: body - name: request - required: true - schema: - $ref: '#/definitions/projectreq.CreateProjectRequest' - produces: - - application/json - responses: - "201": - description: Created - schema: - $ref: '#/definitions/projectres.ProjectResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Create project - tags: - - Projects API - /v1/projects/{project_id}: - delete: - description: Soft-delete a project - parameters: - - description: Project ID - in: path - name: project_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/projectres.ProjectDeletedResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Not Found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Delete project - tags: - - Projects API - get: - description: Get a single project by ID - parameters: - - description: Project ID - in: path - name: project_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/projectres.ProjectResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Not Found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Get project - tags: - - Projects API - patch: - consumes: - - application/json - description: Update project name, instruction, or archived status - parameters: - - description: Project ID - in: path - name: project_id - required: true - type: string - - description: Update request - in: body - name: request - required: true - schema: - $ref: '#/definitions/projectreq.UpdateProjectRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/projectres.ProjectResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Not Found - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Update project - tags: - - Projects API - /v1/prompt-templates/{key}: - get: - consumes: - - application/json - description: Get a prompt template by its unique template key (public endpoint) - parameters: - - description: Template Key - in: path - name: key - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/prompttemplatehandler.PromptTemplateResponse' - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - summary: Get a prompt template by key - tags: - - Prompt Templates - /v1/public/shares/{slug}: - get: - description: Retrieves a publicly shared conversation or message by its slug - parameters: - - description: Share slug - in: path - name: slug - required: true - type: string - produces: - - application/json - responses: - "200": - description: Public share content - schema: - $ref: '#/definitions/shareresponses.PublicShareResponse' - "404": - description: Share not found or revoked - schema: - $ref: '#/definitions/responses.ErrorResponse' - "410": - description: Share has been revoked - schema: - $ref: '#/definitions/responses.ErrorResponse' - summary: Get a public share by slug - tags: - - Public Shares API - head: - description: Checks if a share exists and is accessible (for preloading) - parameters: - - description: Share slug - in: path - name: slug - required: true - type: string - responses: - "200": - description: Share exists and is accessible - "404": - description: Share not found - "410": - description: Share has been revoked - summary: Check if a public share exists - tags: - - Public Shares API - /v1/readyz: - get: - description: Returns the readiness status of the API server. Indicates if the - service is ready to accept traffic. - produces: - - application/json - responses: - "200": - description: Readiness status ready - schema: - additionalProperties: - type: string - type: object - summary: Readiness check endpoint - tags: - - Server API - /v1/shares: - get: - description: Lists all shares (active and revoked) for the authenticated user - across all conversations - parameters: - - default: true - description: Include revoked shares - in: query - name: include_revoked - type: boolean - produces: - - application/json - responses: - "200": - description: List of shares - schema: - $ref: '#/definitions/shareresponses.ShareListResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "403": - description: Forbidden - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: List all shares for the authenticated user - tags: - - Shares API - /v1/shares/{share_id}: - delete: - description: Revokes an active share by its ID, making it inaccessible - parameters: - - description: Share public ID - in: path - name: share_id - required: true - type: string - produces: - - application/json - responses: - "200": - description: Share revoked successfully - schema: - $ref: '#/definitions/shareresponses.ShareDeletedResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "403": - description: Forbidden - schema: - $ref: '#/definitions/responses.ErrorResponse' - "404": - description: Share not found - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Revoke a share by share ID - tags: - - Shares API - /v1/usage/me: - get: - description: Returns token usage summary for the authenticated user within a - date range - parameters: - - description: Start date (YYYY-MM-DD), defaults to 30 days ago - in: query - name: start_date - type: string - - description: End date (YYYY-MM-DD), defaults to today - in: query - name: end_date - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/tokenusage.UsageResponse' - "401": - description: Unauthorized - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - security: - - BearerAuth: [] - summary: Get current user's token usage - tags: - - Usage - /v1/usage/me/daily: - get: - description: Returns daily aggregated token usage for the authenticated user - parameters: - - description: Start date (YYYY-MM-DD), defaults to 30 days ago - in: query - name: start_date - type: string - - description: End date (YYYY-MM-DD), defaults to today - in: query - name: end_date - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - items: - $ref: '#/definitions/tokenusage.DailyAggregate' - type: array - "401": - description: Unauthorized - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - security: - - BearerAuth: [] - summary: Get current user's daily token usage - tags: - - Usage - /v1/usage/projects/{id}: - get: - description: Returns token usage summary for a specific project - parameters: - - description: Project ID - in: path - name: id - required: true - type: string - - description: Start date (YYYY-MM-DD), defaults to 30 days ago - in: query - name: start_date - type: string - - description: End date (YYYY-MM-DD), defaults to today - in: query - name: end_date - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/tokenusage.UsageResponse' - "401": - description: Unauthorized - schema: - additionalProperties: - type: string - type: object - "404": - description: Not Found - schema: - additionalProperties: - type: string - type: object - "500": - description: Internal Server Error - schema: - additionalProperties: - type: string - type: object - security: - - BearerAuth: [] - summary: Get project's token usage - tags: - - Usage - /v1/users/me/settings: - get: - description: Retrieve current user's settings including memory preferences - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/usersettingshandler.UserSettingsResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Get user settings - tags: - - User Settings - patch: - consumes: - - application/json - description: Update current user's settings (partial update supported) - parameters: - - description: Settings to update - in: body - name: settings - required: true - schema: - $ref: '#/definitions/usersettings.UpdateRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/usersettingshandler.UserSettingsResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Update user settings - tags: - - User Settings - /v1/users/me/settings/preferences: - get: - description: Retrieve current user's preferences only - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/usersettingshandler.PreferencesResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Get user preferences - tags: - - User Settings - patch: - consumes: - - application/json - description: Update current user's preferences only - parameters: - - description: Preferences to update - in: body - name: preferences - required: true - schema: - $ref: '#/definitions/usersettingshandler.UpdatePreferencesRequest' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/usersettingshandler.PreferencesResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/responses.ErrorResponse' - "401": - description: Unauthorized - schema: - $ref: '#/definitions/responses.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/responses.ErrorResponse' - security: - - BearerAuth: [] - summary: Update user preferences - tags: - - User Settings - /v1/version: - get: - description: Returns the current build version of the API server and environment - reload timestamp. - produces: - - application/json - responses: - "200": - description: Version information including version number and environment - reload timestamp - schema: - additionalProperties: - type: string - type: object - summary: Get API build version - tags: - - Server API -securityDefinitions: - BearerAuth: - description: Type "Bearer" followed by a space and JWT token. - in: header - name: Authorization - type: apiKey -swagger: "2.0" diff --git a/apps/platform/cli.json b/apps/platform/cli.json deleted file mode 100644 index 4b5daf7e..00000000 --- a/apps/platform/cli.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "aliases": { - "uiDir": "./components/ui", - "componentsDir": "./components", - "blockDir": "./components", - "cssDir": "./styles", - "libDir": "./lib" - }, - "baseDir": "src", - "commands": {} -} diff --git a/apps/platform/components.json b/apps/platform/components.json deleted file mode 100644 index 5dee82eb..00000000 --- a/apps/platform/components.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$schema": "https://ui.shadcn.com/schema.json", - "style": "new-york", - "rsc": true, - "tsx": true, - "tailwind": { - "config": "", - "css": "src/app/global.css", - "baseColor": "neutral", - "cssVariables": true, - "prefix": "" - }, - "iconLibrary": "lucide", - "aliases": { - "components": "@/components", - "utils": "@/lib/utils", - "ui": "@/components/ui", - "lib": "@/lib", - "hooks": "@/hooks" - }, - "registries": {} -} diff --git a/apps/platform/content/docs/api-reference/admin-model-api/meta.json b/apps/platform/content/docs/api-reference/admin-model-api/meta.json deleted file mode 100644 index 4537ea98..00000000 --- a/apps/platform/content/docs/api-reference/admin-model-api/meta.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "title": "Admin Model API", - "pages": [ - "models/catalogs/get", - "models/provider-models/get", - "models/catalogs/bulk-toggle/post", - "models/provider-models/bulk-toggle/post", - "models/catalogs/patch", - "models/provider-models/patch" - ] -} diff --git a/apps/platform/content/docs/api-reference/admin-model-api/models/catalogs/bulk-toggle/post.mdx b/apps/platform/content/docs/api-reference/admin-model-api/models/catalogs/bulk-toggle/post.mdx deleted file mode 100644 index 137420ad..00000000 --- a/apps/platform/content/docs/api-reference/admin-model-api/models/catalogs/bulk-toggle/post.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Bulk enable/disable provider models by catalog IDs or all catalogs -description: >- - Enable or disable provider models for specific catalogs or ALL catalogs, with - optional exception list. Supports "enable/disable all except" patterns - globally or scoped to catalogs. -full: true -_openapi: - method: POST - route: /v1/admin/models/catalogs/bulk-toggle - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Enable or disable provider models for specific catalogs or ALL - catalogs, with optional exception list. Supports "enable/disable all - except" patterns globally or scoped to catalogs. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/admin-model-api/models/catalogs/get.mdx b/apps/platform/content/docs/api-reference/admin-model-api/models/catalogs/get.mdx deleted file mode 100644 index 4033a640..00000000 --- a/apps/platform/content/docs/api-reference/admin-model-api/models/catalogs/get.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Get a model catalog entry -description: >- - Retrieves detailed information about a model catalog entry by its public ID - (supports IDs with slashes) -full: true -_openapi: - method: GET - route: /v1/admin/models/catalogs/{model_public_id} - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Retrieves detailed information about a model catalog entry by its - public ID (supports IDs with slashes) ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/admin-model-api/models/catalogs/patch.mdx b/apps/platform/content/docs/api-reference/admin-model-api/models/catalogs/patch.mdx deleted file mode 100644 index 8e71b1b1..00000000 --- a/apps/platform/content/docs/api-reference/admin-model-api/models/catalogs/patch.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Update a model catalog entry -description: >- - Updates metadata for a model catalog entry. Marks it as manually updated to - prevent auto-sync overwrites. -full: true -_openapi: - method: PATCH - route: /v1/admin/models/catalogs/{model_public_id} - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Updates metadata for a model catalog entry. Marks it as manually - updated to prevent auto-sync overwrites. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/admin-model-api/models/provider-models/bulk-toggle/post.mdx b/apps/platform/content/docs/api-reference/admin-model-api/models/provider-models/bulk-toggle/post.mdx deleted file mode 100644 index 27ebffb1..00000000 --- a/apps/platform/content/docs/api-reference/admin-model-api/models/provider-models/bulk-toggle/post.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: Bulk enable or disable provider models -description: >- - Enables or disables provider models with flexible patterns: enable all, - disable all, enable all except, or disable all except. Optionally filter by - provider. -full: true -_openapi: - method: POST - route: /v1/admin/models/provider-models/bulk-toggle - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Enables or disables provider models with flexible patterns: enable - all, disable all, enable all except, or disable all except. Optionally - filter by provider. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/admin-model-api/models/provider-models/get.mdx b/apps/platform/content/docs/api-reference/admin-model-api/models/provider-models/get.mdx deleted file mode 100644 index 886f0b46..00000000 --- a/apps/platform/content/docs/api-reference/admin-model-api/models/provider-models/get.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: Get a provider model -description: Retrieves detailed information about a provider model by its public ID -full: true -_openapi: - method: GET - route: /v1/admin/models/provider-models/{provider_model_public_id} - toc: [] - structuredData: - headings: [] - contents: - - content: Retrieves detailed information about a provider model by its public ID ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/admin-model-api/models/provider-models/patch.mdx b/apps/platform/content/docs/api-reference/admin-model-api/models/provider-models/patch.mdx deleted file mode 100644 index 332aa0db..00000000 --- a/apps/platform/content/docs/api-reference/admin-model-api/models/provider-models/patch.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Update a provider model -description: >- - Updates configuration for a provider model including pricing, limits, and - feature flags -full: true -_openapi: - method: PATCH - route: /v1/admin/models/provider-models/{provider_model_public_id} - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Updates configuration for a provider model including pricing, limits, - and feature flags ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/admin-provider-api/meta.json b/apps/platform/content/docs/api-reference/admin-provider-api/meta.json deleted file mode 100644 index 6a64c01f..00000000 --- a/apps/platform/content/docs/api-reference/admin-provider-api/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Admin Provider API", - "pages": ["providers/get", "providers/post", "providers/patch"] -} diff --git a/apps/platform/content/docs/api-reference/admin-provider-api/providers/get.mdx b/apps/platform/content/docs/api-reference/admin-provider-api/providers/get.mdx deleted file mode 100644 index e37dcf9f..00000000 --- a/apps/platform/content/docs/api-reference/admin-provider-api/providers/get.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: Get all providers -description: Retrieves all providers with their model counts -full: true -_openapi: - method: GET - route: /v1/admin/providers - toc: [] - structuredData: - headings: [] - contents: - - content: Retrieves all providers with their model counts ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/admin-provider-api/providers/patch.mdx b/apps/platform/content/docs/api-reference/admin-provider-api/providers/patch.mdx deleted file mode 100644 index 9b36e9eb..00000000 --- a/apps/platform/content/docs/api-reference/admin-provider-api/providers/patch.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: Update a provider -description: Updates an existing provider's configuration -full: true -_openapi: - method: PATCH - route: /v1/admin/providers/{provider_public_id} - toc: [] - structuredData: - headings: [] - contents: - - content: Updates an existing provider's configuration ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/admin-provider-api/providers/post.mdx b/apps/platform/content/docs/api-reference/admin-provider-api/providers/post.mdx deleted file mode 100644 index 6e06ab52..00000000 --- a/apps/platform/content/docs/api-reference/admin-provider-api/providers/post.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: Register a provider -description: Registers a new provider and synchronizes its available models. -full: true -_openapi: - method: POST - route: /v1/admin/providers - toc: [] - structuredData: - headings: [] - contents: - - content: Registers a new provider and synchronizes its available models. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/authentication-api/api-keys/delete.mdx b/apps/platform/content/docs/api-reference/authentication-api/api-keys/delete.mdx deleted file mode 100644 index 78ca60b2..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/api-keys/delete.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Delete API key -description: >- - Revokes and deletes an API key by ID. Deleted keys can no longer be used for - authentication. -full: true -_openapi: - method: DELETE - route: /auth/api-keys/{id} - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Revokes and deletes an API key by ID. Deleted keys can no longer be - used for authentication. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/authentication-api/api-keys/get.mdx b/apps/platform/content/docs/api-reference/authentication-api/api-keys/get.mdx deleted file mode 100644 index 53705d11..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/api-keys/get.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: List user's API keys -description: >- - Returns all API keys created by the authenticated user. Key values are not - returned, only metadata. -full: true -_openapi: - method: GET - route: /auth/api-keys - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Returns all API keys created by the authenticated user. Key values are - not returned, only metadata. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/authentication-api/api-keys/post.mdx b/apps/platform/content/docs/api-reference/authentication-api/api-keys/post.mdx deleted file mode 100644 index 5d83155e..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/api-keys/post.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Create API key -description: >- - Creates a new API key for the authenticated user. API keys provide - programmatic access without requiring user credentials. -full: true -_openapi: - method: POST - route: /auth/api-keys - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Creates a new API key for the authenticated user. API keys provide - programmatic access without requiring user credentials. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/authentication-api/callback/get.mdx b/apps/platform/content/docs/api-reference/authentication-api/callback/get.mdx deleted file mode 100644 index 59e7d3a1..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/callback/get.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Handle Keycloak OAuth2 callback -description: >- - Handles the OAuth2 callback from Keycloak, exchanges authorization code for - JWT tokens -full: true -_openapi: - method: GET - route: /auth/callback - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Handles the OAuth2 callback from Keycloak, exchanges authorization - code for JWT tokens ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/authentication-api/guest-login/post.mdx b/apps/platform/content/docs/api-reference/authentication-api/guest-login/post.mdx deleted file mode 100644 index bd909fb1..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/guest-login/post.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Create guest user account -description: >- - Creates a temporary guest user account and returns JWT tokens. Guest users - have limited access and can be upgraded to full accounts later. -full: true -_openapi: - method: POST - route: /auth/guest-login - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Creates a temporary guest user account and returns JWT tokens. Guest - users have limited access and can be upgraded to full accounts later. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/authentication-api/login/get.mdx b/apps/platform/content/docs/api-reference/authentication-api/login/get.mdx deleted file mode 100644 index d941159f..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/login/get.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Initiate Keycloak OAuth2 login -description: >- - Returns the Keycloak authorization URL for frontend to redirect users. - Supports OAuth2 authorization code flow with PKCE. -full: true -_openapi: - method: GET - route: /auth/login - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Returns the Keycloak authorization URL for frontend to redirect users. - Supports OAuth2 authorization code flow with PKCE. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/authentication-api/logout/get.mdx b/apps/platform/content/docs/api-reference/authentication-api/logout/get.mdx deleted file mode 100644 index b23582b5..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/logout/get.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Logout user -description: >- - Revokes the current access token and clears authentication cookies. After - logout, the user must re-authenticate. -full: true -_openapi: - method: GET - route: /auth/logout - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Revokes the current access token and clears authentication cookies. - After logout, the user must re-authenticate. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/authentication-api/me/get.mdx b/apps/platform/content/docs/api-reference/authentication-api/me/get.mdx deleted file mode 100644 index 0a86d44b..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/me/get.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Get current user information -description: >- - Returns the authenticated user's profile information including user ID, email, - roles, and guest status. -full: true -_openapi: - method: GET - route: /auth/me - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Returns the authenticated user's profile information including user - ID, email, roles, and guest status. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/authentication-api/meta.json b/apps/platform/content/docs/api-reference/authentication-api/meta.json deleted file mode 100644 index 845e9ae5..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/meta.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "title": "Authentication API", - "pages": [ - "api-keys/get", - "callback/get", - "login/get", - "logout/get", - "me/get", - "api-keys/post", - "guest-login/post", - "refresh-token/post", - "revoke/post", - "upgrade/post", - "validate-api-key/post", - "validate/post", - "api-keys/delete" - ] -} diff --git a/apps/platform/content/docs/api-reference/authentication-api/refresh-token/post.mdx b/apps/platform/content/docs/api-reference/authentication-api/refresh-token/post.mdx deleted file mode 100644 index ffe1fccb..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/refresh-token/post.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Refresh access token -description: >- - Exchanges a valid refresh token for a new access token. Refresh token must be - provided in Authorization header or refresh_token cookie. -full: true -_openapi: - method: POST - route: /auth/refresh-token - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Exchanges a valid refresh token for a new access token. Refresh token - must be provided in Authorization header or refresh_token cookie. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/authentication-api/revoke/post.mdx b/apps/platform/content/docs/api-reference/authentication-api/revoke/post.mdx deleted file mode 100644 index 32ea73a1..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/revoke/post.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: Revoke Keycloak refresh token -description: Revokes a refresh token to invalidate it -full: true -_openapi: - method: POST - route: /auth/revoke - toc: [] - structuredData: - headings: [] - contents: - - content: Revokes a refresh token to invalidate it ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/authentication-api/upgrade/post.mdx b/apps/platform/content/docs/api-reference/authentication-api/upgrade/post.mdx deleted file mode 100644 index 182643f7..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/upgrade/post.mdx +++ /dev/null @@ -1,22 +0,0 @@ ---- -title: Upgrade guest to permanent account -description: >- - Converts a guest user account to a permanent account with email/password - credentials. Guest flag is removed and user gains full access. -full: true -_openapi: - method: POST - route: /auth/upgrade - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Converts a guest user account to a permanent account with - email/password credentials. Guest flag is removed and user gains full - access. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/authentication-api/validate-api-key/post.mdx b/apps/platform/content/docs/api-reference/authentication-api/validate-api-key/post.mdx deleted file mode 100644 index cb2e2cb8..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/validate-api-key/post.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Validate API key (Kong Plugin) -description: >- - Internal endpoint used by Kong API Gateway to validate API keys. Not intended - for direct client use. -full: true -_openapi: - method: POST - route: /auth/validate-api-key - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Internal endpoint used by Kong API Gateway to validate API keys. Not - intended for direct client use. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/authentication-api/validate/post.mdx b/apps/platform/content/docs/api-reference/authentication-api/validate/post.mdx deleted file mode 100644 index 8ecca942..00000000 --- a/apps/platform/content/docs/api-reference/authentication-api/validate/post.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: Validate Keycloak access token -description: Validates an access token against Keycloak's userinfo endpoint -full: true -_openapi: - method: POST - route: /auth/validate - toc: [] - structuredData: - headings: [] - contents: - - content: Validates an access token against Keycloak's userinfo endpoint ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/backward-compatibility.mdx b/apps/platform/content/docs/api-reference/backward-compatibility.mdx deleted file mode 100644 index 3f280640..00000000 --- a/apps/platform/content/docs/api-reference/backward-compatibility.mdx +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: Backward Compatibility -description: Understand Jan's backward compatibility guarantees and migration details ---- - -# Backward compatibility - -Jan is committed to providing stability to API users by avoiding breaking changes in major API -versions whenever reasonably possible. This includes: - -- The REST API (currently `v1`) -- OpenAI SDK compatibility -- Model families - -> **Model prompting behavior between snapshots is subject to change.** - -> Model outputs are by their nature variable, so expect changes in prompting and model behavior -> between snapshots. For example, if you moved from one model or snapshot version to another, the -> same `system` or `user` messages could function differently between versions. The best way to -> ensure consistent prompting behavior and model output is to use pinned model versions, and to -> implement evals for your applications. - -## Backwards-compatible API changes: - -- Adding new resources (URLs) to the REST API -- Adding new optional API parameters -- Adding new properties to JSON response objects or event data -- Changing the order of properties in a JSON response object -- Changing the length or format of opaque strings, like resource identifiers and UUIDs -- Adding new event types (in either streaming or the Realtime API) - -See the [changelog](https://platform.jan.ai/docs/guides/changelog) for a list of -backwards-compatible changes and rare breaking changes. diff --git a/apps/platform/content/docs/api-reference/chat-completions-api.mdx b/apps/platform/content/docs/api-reference/chat-completions-api.mdx deleted file mode 100644 index 0c0559cf..00000000 --- a/apps/platform/content/docs/api-reference/chat-completions-api.mdx +++ /dev/null @@ -1,31 +0,0 @@ ---- -title: Chat Completions API -description: Overview of Jan’s OpenAI-compatible LLM endpoints. -full: false ---- - -# Chat Completions API - -Jan exposes an OpenAI-compatible Chat Completions surface so existing SDKs and integrations continue to work unchanged. - -- **Base URL**: `http://localhost:8000/llm` -- **Authentication**: Use the same bearer tokens or API keys described in the [Authentication docs](/docs/api-reference/authentication-api/login/get). -- **Response shape**: Mirrors OpenAI’s schema, including tool/function calling payloads and streaming chunks. - -## When to use it - -Reach for this API whenever you need conversational reasoning, structured tool output, or access to enabled models. - -- Discover available models: [`GET /v1/models`](/docs/api-reference/chat-completions-api/models/get) -- Generate chat responses: [`POST /v1/chat/completions`](/docs/api-reference/chat-completions-api/completions/post) - -Both endpoints accept the same payloads as OpenAI, so you can point your client at Jan by just swapping the base URL and API key. - -## Typical workflow - -1. Call `GET /v1/models` to understand which providers/models are enabled. -2. Send `POST /v1/chat/completions` with your conversation, optionally enabling tools/functions. -3. Stream responses by setting `stream: true`—Jan maintains full compatibility with OpenAI’s SSE format. - -Need help troubleshooting? See [Debugging Requests](/docs/api-reference/debugging-requests) for guidance. - diff --git a/apps/platform/content/docs/api-reference/chat-completions-api/completions/post.mdx b/apps/platform/content/docs/api-reference/chat-completions-api/completions/post.mdx deleted file mode 100644 index 2bebbe5c..00000000 --- a/apps/platform/content/docs/api-reference/chat-completions-api/completions/post.mdx +++ /dev/null @@ -1,99 +0,0 @@ ---- -title: Create a chat completion -description: >- - Generates a model response for the given chat conversation. This is a standard - chat completion API that supports both streaming and non-streaming modes - without conversation persistence. - - - **Streaming Mode (stream=true):** - - - Returns Server-Sent Events (SSE) with real-time streaming - - - Streams completion chunks directly from the inference model - - - Final event contains "[DONE]" marker - - - **Non-Streaming Mode (stream=false or omitted):** - - - Returns single JSON response with complete completion - - - Standard OpenAI ChatCompletionResponse format - - - **Storage Options:** - - - `store=true`: Persist the latest input message and assistant response to the - active conversation - - - `store_reasoning=true`: Additionally persist reasoning content provided by - the model - - - When `store` is omitted or false, the conversation remains read-only - - - **Features:** - - - Supports all OpenAI ChatCompletionRequest parameters - - - Optional conversation context for conversation persistence - - - User authentication required - - - Direct inference model integration -full: true -_openapi: - method: POST - route: /v1/chat/completions - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Generates a model response for the given chat conversation. This is a - standard chat completion API that supports both streaming and - non-streaming modes without conversation persistence. - - - **Streaming Mode (stream=true):** - - - Returns Server-Sent Events (SSE) with real-time streaming - - - Streams completion chunks directly from the inference model - - - Final event contains "[DONE]" marker - - - **Non-Streaming Mode (stream=false or omitted):** - - - Returns single JSON response with complete completion - - - Standard OpenAI ChatCompletionResponse format - - - **Storage Options:** - - - `store=true`: Persist the latest input message and assistant - response to the active conversation - - - `store_reasoning=true`: Additionally persist reasoning content - provided by the model - - - When `store` is omitted or false, the conversation remains read-only - - - **Features:** - - - Supports all OpenAI ChatCompletionRequest parameters - - - Optional conversation context for conversation persistence - - - User authentication required - - - Direct inference model integration ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/chat-completions-api/meta.json b/apps/platform/content/docs/api-reference/chat-completions-api/meta.json deleted file mode 100644 index 85148bf5..00000000 --- a/apps/platform/content/docs/api-reference/chat-completions-api/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Chat Completions API", - "pages": ["models/get", "completions/post"] -} diff --git a/apps/platform/content/docs/api-reference/chat-completions-api/models/get.mdx b/apps/platform/content/docs/api-reference/chat-completions-api/models/get.mdx deleted file mode 100644 index d16cea0f..00000000 --- a/apps/platform/content/docs/api-reference/chat-completions-api/models/get.mdx +++ /dev/null @@ -1,23 +0,0 @@ ---- -title: List available models -description: >- - Retrieves a list of available models that can be used for chat completions or - other tasks. Returns either simple model list or detailed list with provider - metadata based on X-PROVIDER-DATA header. -full: true -_openapi: - method: GET - route: /v1/models - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Retrieves a list of available models that can be used for chat - completions or other tasks. Returns either simple model list or - detailed list with provider metadata based on X-PROVIDER-DATA header. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/conversations-api/conv-public-id/items/delete.mdx b/apps/platform/content/docs/api-reference/conversations-api/conv-public-id/items/delete.mdx deleted file mode 100644 index 1be8bb69..00000000 --- a/apps/platform/content/docs/api-reference/conversations-api/conv-public-id/items/delete.mdx +++ /dev/null @@ -1,75 +0,0 @@ ---- -title: Delete a conversation item -description: >- - Delete an item from a conversation. The item will be removed from the - conversation. - - - **Features:** - - - Remove specific item from conversation - - - Automatic ownership verification - - - Returns updated conversation object after deletion - - - Items are permanently removed (not soft delete) - - - **Important:** - - - Deleting an item may affect conversation flow - - - Item IDs are not reused after deletion - - - Other items in conversation remain unchanged - - - Consider creating a new branch instead of deleting items - - - **Response:** - - Returns the conversation object (not the deleted item) -full: true -_openapi: - method: DELETE - route: /v1/conversations/{conv_public_id}/items/{item_id} - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Delete an item from a conversation. The item will be removed from the - conversation. - - - **Features:** - - - Remove specific item from conversation - - - Automatic ownership verification - - - Returns updated conversation object after deletion - - - Items are permanently removed (not soft delete) - - - **Important:** - - - Deleting an item may affect conversation flow - - - Item IDs are not reused after deletion - - - Other items in conversation remain unchanged - - - Consider creating a new branch instead of deleting items - - - **Response:** - - Returns the conversation object (not the deleted item) ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/conversations-api/conv-public-id/items/get.mdx b/apps/platform/content/docs/api-reference/conversations-api/conv-public-id/items/get.mdx deleted file mode 100644 index faa9eb6d..00000000 --- a/apps/platform/content/docs/api-reference/conversations-api/conv-public-id/items/get.mdx +++ /dev/null @@ -1,47 +0,0 @@ ---- -title: Get a conversation item -description: |- - Retrieve a single item from a conversation by item ID - - **Features:** - - Retrieve specific item by ID - - Returns complete item with all content - - Automatic ownership verification via conversation - - Optional include parameter for additional fields - - **Response Fields:** - - `id`: Item ID with `msg_` prefix - - `type`: Item type (message, tool_call, etc.) - - `role`: Role for message items (user, assistant) - - `content`: Item content array - - `status`: Item status (completed, incomplete, etc.) - - `created_at`: Unix timestamp -full: true -_openapi: - method: GET - route: /v1/conversations/{conv_public_id}/items/{item_id} - toc: [] - structuredData: - headings: [] - contents: - - content: |- - Retrieve a single item from a conversation by item ID - - **Features:** - - Retrieve specific item by ID - - Returns complete item with all content - - Automatic ownership verification via conversation - - Optional include parameter for additional fields - - **Response Fields:** - - `id`: Item ID with `msg_` prefix - - `type`: Item type (message, tool_call, etc.) - - `role`: Role for message items (user, assistant) - - `content`: Item content array - - `status`: Item status (completed, incomplete, etc.) - - `created_at`: Unix timestamp ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/conversations-api/conv-public-id/items/post.mdx b/apps/platform/content/docs/api-reference/conversations-api/conv-public-id/items/post.mdx deleted file mode 100644 index 74b51fbc..00000000 --- a/apps/platform/content/docs/api-reference/conversations-api/conv-public-id/items/post.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Create conversation items -description: |- - Add items to a conversation. You may add up to 20 items at a time. - - **Features:** - - Bulk item creation (max 20 items per request) - - Automatic item ID generation with `msg_` prefix - - Items added to conversation's active branch (default: MAIN) - - Returns list of created items with generated IDs - - **Item Types:** - - `message`: User or assistant messages - - `tool_call`: Tool/function call items - - `tool_response`: Tool/function response items - - Other OpenAI-compatible item types - - **Constraints:** - - Maximum 20 items per request - - Each item must have valid type and content - - Items are immutable after creation -full: true -_openapi: - method: POST - route: /v1/conversations/{conv_public_id}/items - toc: [] - structuredData: - headings: [] - contents: - - content: |- - Add items to a conversation. You may add up to 20 items at a time. - - **Features:** - - Bulk item creation (max 20 items per request) - - Automatic item ID generation with `msg_` prefix - - Items added to conversation's active branch (default: MAIN) - - Returns list of created items with generated IDs - - **Item Types:** - - `message`: User or assistant messages - - `tool_call`: Tool/function call items - - `tool_response`: Tool/function response items - - Other OpenAI-compatible item types - - **Constraints:** - - Maximum 20 items per request - - Each item must have valid type and content - - Items are immutable after creation ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/conversations-api/conversations/delete.mdx b/apps/platform/content/docs/api-reference/conversations-api/conversations/delete.mdx deleted file mode 100644 index a617cb1c..00000000 --- a/apps/platform/content/docs/api-reference/conversations-api/conversations/delete.mdx +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: Delete a conversation -description: >- - Delete a conversation (soft delete). Items in the conversation will not be - deleted but will be inaccessible. - - - **Features:** - - - Soft delete (conversation marked as deleted, not physically removed) - - - Items remain in database but become inaccessible - - - Automatic ownership verification - - - Returns deletion confirmation with conversation ID - - - **Response:** - - - `id`: Deleted conversation ID - - - `object`: Always "conversation.deleted" - - - `deleted`: Always true -full: true -_openapi: - method: DELETE - route: /v1/conversations/{conv_public_id} - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Delete a conversation (soft delete). Items in the conversation will - not be deleted but will be inaccessible. - - - **Features:** - - - Soft delete (conversation marked as deleted, not physically removed) - - - Items remain in database but become inaccessible - - - Automatic ownership verification - - - Returns deletion confirmation with conversation ID - - - **Response:** - - - `id`: Deleted conversation ID - - - `object`: Always "conversation.deleted" - - - `deleted`: Always true ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/conversations-api/conversations/get.mdx b/apps/platform/content/docs/api-reference/conversations-api/conversations/get.mdx deleted file mode 100644 index da008601..00000000 --- a/apps/platform/content/docs/api-reference/conversations-api/conversations/get.mdx +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: Get a conversation -description: >- - Retrieve a conversation by ID with ownership verification - - - **Features:** - - - Retrieves conversation metadata including creation timestamp - - - Automatic ownership verification (user can only access their own - conversations) - - - Returns OpenAI-compatible conversation object - - - **Response Fields:** - - - `id`: Conversation ID with `conv_` prefix - - - `object`: Always "conversation" - - - `created_at`: Unix timestamp - - - `metadata`: User-defined key-value pairs -full: true -_openapi: - method: GET - route: /v1/conversations/{conv_public_id} - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Retrieve a conversation by ID with ownership verification - - - **Features:** - - - Retrieves conversation metadata including creation timestamp - - - Automatic ownership verification (user can only access their own - conversations) - - - Returns OpenAI-compatible conversation object - - - **Response Fields:** - - - `id`: Conversation ID with `conv_` prefix - - - `object`: Always "conversation" - - - `created_at`: Unix timestamp - - - `metadata`: User-defined key-value pairs ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/conversations-api/conversations/post.mdx b/apps/platform/content/docs/api-reference/conversations-api/conversations/post.mdx deleted file mode 100644 index b01ef856..00000000 --- a/apps/platform/content/docs/api-reference/conversations-api/conversations/post.mdx +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: Create a conversation -description: >- - Create a new conversation to store and retrieve conversation state across - Response API calls - - - **Features:** - - - Create conversation with optional metadata (max 16 key-value pairs) - - - Add up to 20 initial items to the conversation - - - Returns conversation ID with `conv_` prefix - - - Supports OpenAI Conversations API format - - - **Metadata Constraints:** - - - Maximum 16 key-value pairs - - - Keys: max 64 characters - - - Values: max 512 characters -full: true -_openapi: - method: POST - route: /v1/conversations - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Create a new conversation to store and retrieve conversation state - across Response API calls - - - **Features:** - - - Create conversation with optional metadata (max 16 key-value pairs) - - - Add up to 20 initial items to the conversation - - - Returns conversation ID with `conv_` prefix - - - Supports OpenAI Conversations API format - - - **Metadata Constraints:** - - - Maximum 16 key-value pairs - - - Keys: max 64 characters - - - Values: max 512 characters ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/conversations-api/meta.json b/apps/platform/content/docs/api-reference/conversations-api/meta.json deleted file mode 100644 index 44aa19f8..00000000 --- a/apps/platform/content/docs/api-reference/conversations-api/meta.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "title": "Conversations API", - "pages": [ - "conv-public-id/items/get", - "conversations/get", - "conv-public-id/items/post", - "conversations/post", - "conv-public-id/items/delete", - "conversations/delete" - ] -} diff --git a/apps/platform/content/docs/api-reference/debugging-requests.mdx b/apps/platform/content/docs/api-reference/debugging-requests.mdx deleted file mode 100644 index f4319858..00000000 --- a/apps/platform/content/docs/api-reference/debugging-requests.mdx +++ /dev/null @@ -1,561 +0,0 @@ ---- -title: Debugging Requests -description: Learn how to debug API requests and troubleshoot issues ---- - -# Debugging Requests - -This guide will help you debug API requests and troubleshoot common issues. - -## Request Logging - -### Enable Request Logging - -Add verbose logging to see detailed request information: - -```python -import logging -from Jan import Jan - -# Enable debug logging -logging.basicConfig(level=logging.DEBUG) - -client = Jan(api_key="your-api-key") -response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello"}] -) -``` - -### Request/Response Headers - -Log all request and response headers: - -```python -import requests -from Jan import Jan - -# Custom session with logging -session = requests.Session() - -# Add request logging -def log_request(req): - print(f"Request: {req.method} {req.url}") - print(f"Headers: {dict(req.headers)}") - if req.body: - print(f"Body: {req.body.decode()}") - -# Add response logging -def log_response(resp): - print(f"Response: {resp.status_code}") - print(f"Headers: {dict(resp.headers)}") - print(f"Body: {resp.text}") - -session.hooks['response'] = log_response - -client = Jan(api_key="your-api-key", session=session) -``` - -## Common Issues - -### 1. Authentication Errors - -**Error**: `401 Unauthorized` - -**Debug Steps**: - -```python -# Check API key format -api_key = "sk-Jan-1234567890abcdef" -print(f"API key length: {len(api_key)}") -print(f"API key starts with: {api_key[:8]}") - -# Test authentication -from Jan import Jan, APIError - -try: - client = Jan(api_key=api_key) - response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "test"}] - ) - print("Authentication successful") -except APIError as e: - print(f"Authentication failed: {e.message}") - print(f"Error code: {e.code}") -``` - -**Common Causes**: - -- Invalid API key -- Expired API key -- Missing Authorization header -- Incorrect header format - -### 2. Rate Limiting - -**Error**: `429 Too Many Requests` - -**Debug Steps**: - -```python -import time -from Jan import Jan, APIError - -client = Jan(api_key="your-api-key") - -# Check rate limit headers -response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "test"}] -) - -print(f"Rate limit: {response.headers.get('X-RateLimit-Limit')}") -print(f"Remaining: {response.headers.get('X-RateLimit-Remaining')}") -print(f"Reset time: {response.headers.get('X-RateLimit-Reset')}") -``` - -**Handling Rate Limits**: - -```python -def make_request_with_retry(client, max_retries=3): - for attempt in range(max_retries): - try: - return client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "test"}] - ) - except APIError as e: - if e.code == "rate_limit_exceeded" and attempt < max_retries - 1: - wait_time = 2 ** attempt # Exponential backoff - print(f"Rate limited, waiting {wait_time} seconds...") - time.sleep(wait_time) - else: - raise -``` - -### 3. Model Not Found - -**Error**: `400 Bad Request` with `model_not_found` - -**Debug Steps**: - -```python -# List available models -models = client.models.list() -print("Available models:") -for model in models.data: - print(f"- {model.id}") - -# Check model name -model_name = "gpt-4" -if model_name not in [m.id for m in models.data]: - print(f"Model '{model_name}' not found") - print("Available models:", [m.id for m in models.data]) -``` - -### 4. Invalid Parameters - -**Error**: `400 Bad Request` with `invalid_parameter` - -**Debug Steps**: - -```python -# Validate request parameters -request_data = { - "model": "gpt-4", - "messages": [{"role": "user", "content": "Hello"}], - "max_tokens": 100, - "temperature": 0.7 -} - -# Check required fields -required_fields = ["model", "messages"] -for field in required_fields: - if field not in request_data: - print(f"Missing required field: {field}") - -# Check message format -for i, message in enumerate(request_data["messages"]): - if "role" not in message: - print(f"Message {i} missing 'role' field") - if "content" not in message: - print(f"Message {i} missing 'content' field") - if message["role"] not in ["user", "assistant", "system"]: - print(f"Message {i} has invalid role: {message['role']}") -``` - -## Request Validation - -### Validate Request Before Sending - -```python -def validate_request(request_data): - errors = [] - - # Check required fields - if "model" not in request_data: - errors.append("Missing required field: model") - - if "messages" not in request_data: - errors.append("Missing required field: messages") - - # Check messages format - if "messages" in request_data: - messages = request_data["messages"] - if not isinstance(messages, list): - errors.append("Messages must be a list") - else: - for i, message in enumerate(messages): - if not isinstance(message, dict): - errors.append(f"Message {i} must be a dictionary") - else: - if "role" not in message: - errors.append(f"Message {i} missing 'role' field") - if "content" not in message: - errors.append(f"Message {i} missing 'content' field") - - # Check optional fields - if "max_tokens" in request_data: - if not isinstance(request_data["max_tokens"], int) or request_data["max_tokens"] <= 0: - errors.append("max_tokens must be a positive integer") - - if "temperature" in request_data: - temp = request_data["temperature"] - if not isinstance(temp, (int, float)) or temp < 0 or temp > 2: - errors.append("temperature must be between 0 and 2") - - return errors - -# Use validation -request_data = { - "model": "gpt-4", - "messages": [{"role": "user", "content": "Hello"}] -} - -errors = validate_request(request_data) -if errors: - print("Validation errors:") - for error in errors: - print(f"- {error}") -else: - print("Request is valid") -``` - -## Response Debugging - -### Inspect Response Details - -```python -response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello"}] -) - -# Print response details -print(f"Status Code: {response.status_code}") -print(f"Response ID: {response.id}") -print(f"Model: {response.model}") -print(f"Created: {response.created}") -print(f"Usage: {response.usage}") -print(f"Choices: {len(response.choices)}") - -# Print each choice -for i, choice in enumerate(response.choices): - print(f"Choice {i}:") - print(f" Index: {choice.index}") - print(f" Message: {choice.message.content}") - print(f" Finish Reason: {choice.finish_reason}") -``` - -### Debug Streaming Responses - -```python -stream = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Write a story"}], - stream=True -) - -chunk_count = 0 -for chunk in stream: - chunk_count += 1 - print(f"Chunk {chunk_count}:") - print(f" ID: {chunk.id}") - print(f" Object: {chunk.object}") - - if chunk.choices: - for choice in chunk.choices: - if choice.delta.content: - print(f" Content: {choice.delta.content}") - if choice.delta.role: - print(f" Role: {choice.delta.role}") - if choice.finish_reason: - print(f" Finish Reason: {choice.finish_reason}") - - print() -``` - -## Network Issues - -### Check Network Connectivity - -```python -import requests -import time - -def check_connectivity(): - try: - response = requests.get("https://api.Jan.ai/v1/version", timeout=10) - print(f"API is reachable: {response.status_code}") - return True - except requests.exceptions.Timeout: - print("Request timed out") - return False - except requests.exceptions.ConnectionError: - print("Connection error") - return False - except Exception as e: - print(f"Unexpected error: {e}") - return False - -# Test connectivity -if check_connectivity(): - print("Network connectivity is good") -else: - print("Network connectivity issues detected") -``` - -### Handle Network Errors - -```python -import requests -from Jan import Jan, APIError - -def make_request_with_retry(client, max_retries=3): - for attempt in range(max_retries): - try: - return client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "test"}] - ) - except requests.exceptions.Timeout: - if attempt < max_retries - 1: - wait_time = 2 ** attempt - print(f"Timeout, retrying in {wait_time} seconds...") - time.sleep(wait_time) - else: - raise - except requests.exceptions.ConnectionError: - if attempt < max_retries - 1: - wait_time = 2 ** attempt - print(f"Connection error, retrying in {wait_time} seconds...") - time.sleep(wait_time) - else: - raise - except APIError as e: - # Don't retry API errors - raise -``` - -## Performance Debugging - -### Measure Request Latency - -```python -import time -from Jan import Jan - -client = Jan(api_key="your-api-key") - -# Measure request time -start_time = time.time() -response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello"}] -) -end_time = time.time() - -latency = end_time - start_time -print(f"Request latency: {latency:.2f} seconds") -print(f"Tokens generated: {response.usage.completion_tokens}") -print(f"Tokens per second: {response.usage.completion_tokens / latency:.2f}") -``` - -### Monitor Token Usage - -```python -def monitor_token_usage(client, requests_count=10): - total_tokens = 0 - total_cost = 0 - - for i in range(requests_count): - response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": f"Request {i+1}"}] - ) - - usage = response.usage - total_tokens += usage.total_tokens - - # Calculate cost (example rates) - cost = usage.total_tokens * 0.00003 # $0.03 per 1K tokens - total_cost += cost - - print(f"Request {i+1}: {usage.total_tokens} tokens, ${cost:.4f}") - - print(f"Total tokens: {total_tokens}") - print(f"Total cost: ${total_cost:.4f}") - print(f"Average tokens per request: {total_tokens / requests_count:.2f}") - -monitor_token_usage(client) -``` - -## Debugging Tools - -### Request Interceptor - -```python -import requests -from Jan import Jan - -class DebugSession(requests.Session): - def request(self, method, url, **kwargs): - print(f"Making {method} request to {url}") - print(f"Headers: {kwargs.get('headers', {})}") - if 'json' in kwargs: - print(f"Body: {kwargs['json']}") - - response = super().request(method, url, **kwargs) - - print(f"Response status: {response.status_code}") - print(f"Response headers: {dict(response.headers)}") - - return response - -# Use debug session -session = DebugSession() -client = Jan(api_key="your-api-key", session=session) -``` - -### Error Logger - -```python -import logging -from Jan import Jan, APIError - -# Set up logging -logging.basicConfig( - level=logging.DEBUG, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', - handlers=[ - logging.FileHandler('debug.log'), - logging.StreamHandler() - ] -) - -logger = logging.getLogger(__name__) - -client = Jan(api_key="your-api-key") - -try: - response = client.chat.completions.create( - model="gpt-4", - messages=[{"role": "user", "content": "Hello"}] - ) - logger.info(f"Request successful: {response.id}") -except APIError as e: - logger.error(f"API Error: {e.code} - {e.message}") -except Exception as e: - logger.error(f"Unexpected error: {e}") -``` - -## Best Practices - -### 1. Always Handle Errors - -```python -from Jan import Jan, APIError - -try: - response = client.chat.completions.create(...) -except APIError as e: - # Log error details - logger.error(f"API Error: {e.code} - {e.message}") - - # Handle specific error types - if e.code == "rate_limit_exceeded": - # Implement backoff - pass - elif e.code == "invalid_parameter": - # Fix request parameters - pass - else: - # Handle other errors - pass -``` - -### 2. Validate Inputs - -```python -def validate_message(message): - if not isinstance(message, dict): - raise ValueError("Message must be a dictionary") - - if "role" not in message: - raise ValueError("Message missing 'role' field") - - if "content" not in message: - raise ValueError("Message missing 'content' field") - - if message["role"] not in ["user", "assistant", "system"]: - raise ValueError(f"Invalid role: {message['role']}") - - return True -``` - -### 3. Use Timeouts - -```python -import requests -from Jan import Jan - -# Set reasonable timeouts -session = requests.Session() -session.timeout = 30 # 30 second timeout - -client = Jan(api_key="your-api-key", session=session) -``` - -## Troubleshooting Checklist - -### Before Making Requests - -- [ ] API key is valid and not expired -- [ ] Request format is correct -- [ ] Required fields are present -- [ ] Parameter values are within valid ranges -- [ ] Network connectivity is working - -### When Requests Fail - -- [ ] Check error message and code -- [ ] Verify request parameters -- [ ] Check rate limits -- [ ] Verify API key permissions -- [ ] Test with minimal request -- [ ] Check API status page - -### Performance Issues - -- [ ] Measure request latency -- [ ] Check token usage -- [ ] Monitor rate limits -- [ ] Use appropriate models -- [ ] Optimize prompts - -## Next Steps - -- [API Introduction](/docs/api-reference/introduction) - Learn about the API -- [Authentication](/docs/api-reference/authentication-api) - Understand authentication -- [Error Handling](/docs/api-reference/introduction#error-handling) - Handle errors properly diff --git a/apps/platform/content/docs/api-reference/introduction.mdx b/apps/platform/content/docs/api-reference/introduction.mdx deleted file mode 100644 index 2f3e0f21..00000000 --- a/apps/platform/content/docs/api-reference/introduction.mdx +++ /dev/null @@ -1,446 +0,0 @@ ---- -title: API Reference -description: Complete API documentation for Jan Server services -full: false ---- - -# API Reference - -Complete API documentation for Jan Server services. - -## Overview - -Jan Server provides four main APIs: - -- **[LLM API](#llm-api)** - OpenAI-compatible chat completions, conversations, and models -- **[Response API](#response-api)** - Multi-step tool orchestration and AI responses -- **[Media API](#media-api)** - Image upload, storage, and management -- **[MCP Tools API](#mcp-tools-api)** - Model Context Protocol tools for search, scraping, and execution - -## Base URLs - -| Environment | LLM API | Response API | Media API | MCP Tools | Gateway | -|-------------|---------|--------------|-----------|-----------|---------| -| **Local** | http://localhost:8080 | http://localhost:8082 | http://localhost:8285 | http://localhost:8091 | http://localhost:8000 | -| **Docker** | http://llm-api:8080 | http://response-api:8082 | http://media-api:8285 | http://mcp-tools:8091 | http://kong:8000 | - -**Recommended**: Point all public clients at the Kong gateway (port 8000) for consistent authentication, rate limiting, and routing. - -## Authentication - -All API endpoints require authentication via Kong Gateway (port 8000). - -### Two Ways to Authenticate - -**1. Bearer Token** (Recommended for development): -```bash -# Get guest token -curl -X POST http://localhost:8000/llm/auth/guest-login - -# Response -{ - "access_token": "eyJhbGci...", - "refresh_token": "eyJhbGci...", - "expires_in": 300 -} - -# Use token -curl -H "Authorization: Bearer " \ - http://localhost:8000/v1/models -``` - -**2. API Key** (Recommended for production): -```bash -curl -H "X-API-Key: sk_your_api_key_here" \ - http://localhost:8000/v1/chat/completions -``` - -### Token Refresh - -Guest tokens expire after 5 minutes. Use the refresh token to get a new access token: - -```bash -curl -X POST http://localhost:8000/llm/auth/refresh \ - -H "Content-Type: application/json" \ - -d '{"refresh_token": "your-refresh-token"}' -``` - -## Quick Start - -### 1. Get Authentication Token - -```bash -TOKEN=$(curl -X POST http://localhost:8000/llm/auth/guest-login | jq -r '.access_token') -``` - -### 2. Chat Completion - -```bash -curl -X POST http://localhost:8000/v1/chat/completions \ - -H "Authorization: Bearer $TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "model": "qwen2.5-0.5b-instruct", - "messages": [ - {"role": "user", "content": "Hello!"} - ] - }' -``` - -### 3. List Models - -```bash -curl -H "Authorization: Bearer $TOKEN" \ - http://localhost:8000/v1/models -``` - -### 4. Use MCP Tools - -```bash -curl -X POST http://localhost:8000/v1/mcp \ - -H "Authorization: Bearer $TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "jsonrpc": "2.0", - "method": "tools/call", - "params": { - "name": "google_search", - "arguments": {"q": "AI news"} - } - }' -``` - -## LLM API - -OpenAI-compatible API for chat completions, conversations, and models. - -**Port:** 8080 (direct) / 8000 (via gateway) - -**Features:** -- Chat completions with streaming -- Conversation management -- Project organization -- Multi-model support -- Vision/image support via `jan_*` IDs - -**Endpoints:** -- `POST /v1/chat/completions` - Generate AI responses -- `GET /v1/models` - List available models -- `GET /v1/conversations` - List conversations -- `POST /v1/conversations` - Create conversation -- `GET /v1/projects` - List projects -- `POST /v1/projects` - Create project - -[View LLM API Documentation →](/docs/api-reference/chat-completions-api) - -## Response API - -Multi-step orchestration engine that executes tools and generates responses. - -**Port:** 8082 (direct) / 8000 (via gateway) - -**Features:** -- Chain multiple tools together -- Up to 8 execution steps -- Automatic tool selection -- Final answer generation - -**Endpoints:** -- `POST /v1/responses` - Create response with tool execution - - - -## Media API - -Image upload and storage with S3 integration. - -**Port:** 8285 (direct) / 8000 (via gateway) - -**Features:** -- Upload from URL or base64 -- S3 cloud storage -- `jan_*` ID system -- Presigned URL generation -- Duplicate detection - -**Endpoints:** -- `POST /v1/media/upload` - Upload media -- `POST /v1/media/resolve` - Resolve `jan_*` IDs -- `POST /v1/media/presigned-url` - Get presigned URL - - - -## MCP Tools API - -Model Context Protocol tools for search, scraping, vector search, and code execution. - -**Port:** 8091 (direct) / 8000 (via gateway) - -**Available Tools:** -- `google_search` - Web search via Serper/SearXNG -- `scrape` - Fetch and parse web pages -- `file_search_index` / `file_search_query` - Vector search -- `python_exec` - Sandboxed Python execution - -**Endpoints:** -- `POST /v1/mcp` (method: `tools/list`) - List tools -- `POST /v1/mcp` (method: `tools/call`) - Execute tool - - - -## Response Format - -All successful responses return JSON: - -```json -{ - "data": {...}, - "meta": {...} -} -``` - -## Error Format - -All errors follow this structure: - -```json -{ - "error": { - "type": "invalid_request_error", - "code": "invalid_parameter", - "message": "Parameter 'model' is required", - "param": "model", - "request_id": "req_123xyz" - } -} -``` - -## Error Types - -| Type | Description | HTTP Status | -|------|-------------|-------------| -| `invalid_request_error` | Invalid request parameters | 400 | -| `auth_error` | Authentication failed | 401 | -| `permission_error` | Insufficient permissions | 403 | -| `not_found_error` | Resource not found | 404 | -| `rate_limit_error` | Too many requests | 429 | -| `internal_error` | Server error | 500 | - -## Rate Limiting - -Requests through Kong Gateway are rate-limited: -- **Development**: 100 requests/minute per IP -- Headers: `X-RateLimit-Limit-minute`, `X-RateLimit-Remaining-minute` -- Exceeding limit returns HTTP 429 - -## OpenAI SDK Compatibility - -Jan Server is fully compatible with OpenAI SDKs. Simply point the SDK to your Jan Server instance: - -**Python:** -```python -from openai import OpenAI - -client = OpenAI( - base_url="http://localhost:8000/v1", - api_key="your-token" -) - -response = client.chat.completions.create( - model="qwen2.5-0.5b-instruct", - messages=[{"role": "user", "content": "Hello!"}] -) -``` - -**Node.js:** -```javascript -import OpenAI from 'openai'; - -const client = new OpenAI({ - baseURL: 'http://localhost:8000/v1', - apiKey: 'your-token' -}); - -const response = await client.chat.completions.create({ - model: 'qwen2.5-0.5b-instruct', - messages: [{ role: 'user', content: 'Hello!' }] -}); -``` - -## Next Steps - -- [Authentication](/docs/api-reference/authentication-api) - Learn about authentication methods -- [Chat Completions](/docs/api-reference/chat-completions-api) - Generate AI responses -- [Getting Started](/docs/getting-started/installation) - Set up Jan Server -- [Developer Guides](/docs/guides/development) - Development workflows - -## Need Help? - -- [Operations & Troubleshooting](/docs/guides/operations) -- [GitHub Issues](https://github.com/janhq/jan-server/issues) -- [GitHub Discussions](https://github.com/janhq/jan-server/discussions) - -## Base URL - -All API requests should be made to: - -``` -https://api.Jan.ai -``` - -## Authentication - -### API Key Authentication - -Include your API key in the Authorization header: - -```bash -Authorization: Bearer sk-Jan-... -``` - -### OAuth 2.0 Authentication - -For applications that need user-specific access: - -```bash -Authorization: Bearer -``` - -## API Versioning - -All API endpoints are versioned. The current version is `v1`: - -``` -https://api.Jan.ai/v1/chat/completions -``` - -## Request Format - -All requests must include: - -- `Content-Type: application/json` header -- JSON request body -- Proper authentication headers - -## Response Format - -All responses are returned as JSON with the following structure: - -```json -{ - "id": "resp_1234567890", - "object": "response", - "created": 1640995200, - "model": "gpt-4", - "choices": [ - { - "index": 0, - "message": { - "role": "assistant", - "content": "Hello! How can I help you today?" - }, - "finish_reason": "stop" - } - ], - "usage": { - "prompt_tokens": 10, - "completion_tokens": 15, - "total_tokens": 25 - } -} -``` - -## Error Handling - -### Error Response Format - -```json -{ - "error": { - "message": "Invalid request parameters", - "type": "invalid_request_error", - "code": "invalid_parameter", - "param": "model" - } -} -``` - -### Common Error Codes - -| Code | Description | -| --------------------- | ----------------------------- | -| `invalid_parameter` | Invalid request parameters | -| `rate_limit_exceeded` | Rate limit exceeded | -| `insufficient_quota` | Insufficient API quota | -| `model_not_found` | Specified model not available | -| `unauthorized` | Invalid or missing API key | -| `forbidden` | Access denied | -| `not_found` | Resource not found | -| `server_error` | Internal server error | - -## Rate Limits - -Rate limits are applied per API key: - -| Tier | Requests per Minute | Requests per Day | -| ---------- | ------------------- | ---------------- | -| Free | 3 | 50 | -| Pro | 60 | 100,000 | -| Enterprise | Custom | Custom | - -Rate limit headers are included in responses: - -```http -X-RateLimit-Limit: 60 -X-RateLimit-Remaining: 59 -X-RateLimit-Reset: 1640995260 -``` - -## Pagination - -List endpoints support pagination: - -```bash -GET /v1/conversations?limit=10&offset=20 -``` - -Response includes pagination metadata: - -```json -{ - "object": "list", - "data": [...], - "has_more": true, - "total_count": 150 -} -``` - -## Streaming - -Some endpoints support streaming responses: - -```bash -curl -X POST https://api.Jan.ai/v1/chat/completions \ - -H "Authorization: Bearer YOUR_API_KEY" \ - -H "Content-Type: application/json" \ - -d '{ - "model": "gpt-4", - "messages": [{"role": "user", "content": "Hello"}], - "stream": true - }' -``` - -Streaming responses use Server-Sent Events (SSE): - -``` -data: {"id":"resp_123","object":"response","choices":[{"delta":{"content":"Hello"}}]} - -data: {"id":"resp_123","object":"response","choices":[{"delta":{"content":" there"}}]} - -data: [DONE] -``` - -## Next Steps - -- [Authentication](/docs/api-reference/authentication-api) - Learn about authentication methods -- [Conversations](/docs/api-reference/conversations-api) - Work with multi-turn conversations diff --git a/apps/platform/content/docs/api-reference/meta.json b/apps/platform/content/docs/api-reference/meta.json deleted file mode 100644 index db691f52..00000000 --- a/apps/platform/content/docs/api-reference/meta.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "title": "API Reference", - "pages": [ - "introduction", - "authentication-api", - "chat-completions-api", - "conversations-api", - "projects-api", - "model-api", - "admin-model-api", - "admin-provider-api", - "server-api", - "debugging-requests", - "backward-compatibility" - ], - "description": "Jan Server API Reference", - "root": false -} diff --git a/apps/platform/content/docs/api-reference/model-api/meta.json b/apps/platform/content/docs/api-reference/model-api/meta.json deleted file mode 100644 index 90bc8acc..00000000 --- a/apps/platform/content/docs/api-reference/model-api/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Model API", - "pages": ["models/catalogs/get", "models/providers/get"] -} diff --git a/apps/platform/content/docs/api-reference/model-api/models/catalogs/get.mdx b/apps/platform/content/docs/api-reference/model-api/models/catalogs/get.mdx deleted file mode 100644 index 2e7608d7..00000000 --- a/apps/platform/content/docs/api-reference/model-api/models/catalogs/get.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Get a model catalog entry -description: >- - Retrieves detailed information about a model catalog entry by its public ID - (supports IDs with slashes like openrouter/nova-lite-v1) -full: true -_openapi: - method: GET - route: /v1/models/catalogs/{model_public_id} - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Retrieves detailed information about a model catalog entry by its - public ID (supports IDs with slashes like openrouter/nova-lite-v1) ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/model-api/models/providers/get.mdx b/apps/platform/content/docs/api-reference/model-api/models/providers/get.mdx deleted file mode 100644 index 92da2c82..00000000 --- a/apps/platform/content/docs/api-reference/model-api/models/providers/get.mdx +++ /dev/null @@ -1,19 +0,0 @@ ---- -title: List model providers -description: Retrieves a list of available model providers that can be used for inference. -full: true -_openapi: - method: GET - route: /v1/models/providers - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Retrieves a list of available model providers that can be used for - inference. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/projects-api/meta.json b/apps/platform/content/docs/api-reference/projects-api/meta.json deleted file mode 100644 index 639069cd..00000000 --- a/apps/platform/content/docs/api-reference/projects-api/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Projects API", - "pages": ["projects/get", "projects/post", "projects/patch", "projects/delete"] -} diff --git a/apps/platform/content/docs/api-reference/projects-api/projects/delete.mdx b/apps/platform/content/docs/api-reference/projects-api/projects/delete.mdx deleted file mode 100644 index 85fb5e08..00000000 --- a/apps/platform/content/docs/api-reference/projects-api/projects/delete.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: Delete project -description: Soft-delete a project -full: true -_openapi: - method: DELETE - route: /v1/projects/{project_id} - toc: [] - structuredData: - headings: [] - contents: - - content: Soft-delete a project ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/projects-api/projects/get.mdx b/apps/platform/content/docs/api-reference/projects-api/projects/get.mdx deleted file mode 100644 index 0d108619..00000000 --- a/apps/platform/content/docs/api-reference/projects-api/projects/get.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: Get project -description: Get a single project by ID -full: true -_openapi: - method: GET - route: /v1/projects/{project_id} - toc: [] - structuredData: - headings: [] - contents: - - content: Get a single project by ID ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/projects-api/projects/patch.mdx b/apps/platform/content/docs/api-reference/projects-api/projects/patch.mdx deleted file mode 100644 index 2d1ebd69..00000000 --- a/apps/platform/content/docs/api-reference/projects-api/projects/patch.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: Update project -description: Update project name, instruction, or archived status -full: true -_openapi: - method: PATCH - route: /v1/projects/{project_id} - toc: [] - structuredData: - headings: [] - contents: - - content: Update project name, instruction, or archived status ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/projects-api/projects/post.mdx b/apps/platform/content/docs/api-reference/projects-api/projects/post.mdx deleted file mode 100644 index 7bce88a7..00000000 --- a/apps/platform/content/docs/api-reference/projects-api/projects/post.mdx +++ /dev/null @@ -1,17 +0,0 @@ ---- -title: Create project -description: Create a new project for grouping conversations -full: true -_openapi: - method: POST - route: /v1/projects - toc: [] - structuredData: - headings: [] - contents: - - content: Create a new project for grouping conversations ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/server-api/healthz/get.mdx b/apps/platform/content/docs/api-reference/server-api/healthz/get.mdx deleted file mode 100644 index 3e5a36a4..00000000 --- a/apps/platform/content/docs/api-reference/server-api/healthz/get.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Health check endpoint -description: >- - Returns the health status of the API server. Used by orchestrators and - monitoring systems. -full: true -_openapi: - method: GET - route: /v1/healthz - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Returns the health status of the API server. Used by orchestrators and - monitoring systems. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/server-api/meta.json b/apps/platform/content/docs/api-reference/server-api/meta.json deleted file mode 100644 index b6a89ea5..00000000 --- a/apps/platform/content/docs/api-reference/server-api/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Server API", - "pages": ["healthz/get", "readyz/get", "version/get"] -} diff --git a/apps/platform/content/docs/api-reference/server-api/readyz/get.mdx b/apps/platform/content/docs/api-reference/server-api/readyz/get.mdx deleted file mode 100644 index f56c8bd8..00000000 --- a/apps/platform/content/docs/api-reference/server-api/readyz/get.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Readiness check endpoint -description: >- - Returns the readiness status of the API server. Indicates if the service is - ready to accept traffic. -full: true -_openapi: - method: GET - route: /v1/readyz - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Returns the readiness status of the API server. Indicates if the - service is ready to accept traffic. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/api-reference/server-api/version/get.mdx b/apps/platform/content/docs/api-reference/server-api/version/get.mdx deleted file mode 100644 index 86416925..00000000 --- a/apps/platform/content/docs/api-reference/server-api/version/get.mdx +++ /dev/null @@ -1,21 +0,0 @@ ---- -title: Get API build version -description: >- - Returns the current build version of the API server and environment reload - timestamp. -full: true -_openapi: - method: GET - route: /v1/version - toc: [] - structuredData: - headings: [] - contents: - - content: >- - Returns the current build version of the API server and environment - reload timestamp. ---- - -{/* This file was generated by Fumadocs. Do not edit this file directly. Any changes should be made by running the generation command again. */} - - \ No newline at end of file diff --git a/apps/platform/content/docs/architecture/data-flow.mdx b/apps/platform/content/docs/architecture/data-flow.mdx deleted file mode 100644 index 1cdf6aba..00000000 --- a/apps/platform/content/docs/architecture/data-flow.mdx +++ /dev/null @@ -1,50 +0,0 @@ ---- -title: "Data Flow Reference" ---- - -# Data Flow Reference - -## 1. Chat Completion (LLM API) -1. **Client** calls Kong Gateway `POST /v1/chat/completions` with Bearer token. -2. **Kong** forwards to `llm-api:8080` (internal DNS) and injects request headers. -3. **LLM API**: - - Validates JWT via Keycloak JWKS. - - Resolves any `jan_*` media IDs by calling Media API `/v1/media/resolve`. - - Selects a provider (local vLLM or configured upstream) and forwards the request. -4. **Provider** (vLLM) streams tokens back to LLM API. -5. **LLM API** streams data to the client (SSE) via Kong and persists conversation rows in PostgreSQL. - -## 2. Response API Orchestration -1. **Client** calls `POST /v1/responses`. -2. **Response API** looks up conversation state and enqueues tool steps. -3. For each tool call: - - Executes JSON-RPC request against MCP Tools (`/v1/mcp`). - - Records execution metadata in PostgreSQL. - - Applies depth/timeout limits (`MAX_TOOL_EXECUTION_DEPTH`, `TOOL_EXECUTION_TIMEOUT`). -4. Final synthesis request is sent to LLM API. -5. Completed response is stored and streamed back to the caller (SSE `response.*` events). - -## 3. Media Upload and Resolution -1. Client uploads via: - - `POST /v1/media` (server-proxied, data URL or remote fetch), or - - `POST /v1/media/prepare-upload` followed by direct S3 upload. -2. Media API stores metadata rows and issues `jan_` IDs. -3. Other services reference those IDs instead of exposing raw S3 URLs. -4. Before inference, LLM API calls `/v1/media/resolve` with the request payload; Media API rewrites each placeholder with a fresh presigned URL. - -## 4. MCP Tool Execution -1. Response API or external clients send MCP JSON-RPC requests to `mcp-tools:8091`. -2. MCP Tools selects the proper backend: - - Web search -> Serper or SearXNG (via redis-searxng cache) - - Scrape -> HTTP fetcher with metadata - - File search -> vector-store service - - Python exec -> SandboxFusion container -3. Results are returned synchronously; streaming support is planned via incremental notifications. - -## 5. Observability Pipeline -1. Services emit traces and metrics via OTLP (4317). -2. The OpenTelemetry Collector forwards metrics to Prometheus and traces to Jaeger. -3. Logs are structured JSON printed to stdout; Docker/ Kubernetes aggregates them for your logging stack. -4. Grafana dashboards connect to Prometheus and Jaeger for live inspection. - -Use this file when onboarding engineers or mapping changes that span multiple services. diff --git a/apps/platform/content/docs/architecture/index.mdx b/apps/platform/content/docs/architecture/index.mdx deleted file mode 100644 index 62d6d263..00000000 --- a/apps/platform/content/docs/architecture/index.mdx +++ /dev/null @@ -1,96 +0,0 @@ ---- -title: "Jan Server Architecture" ---- - -# Jan Server Architecture - -## Overview - -Jan Server is built from multiple small services (microservices) that work together. Each service has a specific job. - -## Main Parts - -1. **[System Design](./system-design)** - How all the pieces fit together -2. **[Services](./services)** - What each service does -3. **[Data Flow](./data-flow)** - How requests move through the system -4. **[Security](./security)** - How we keep things secure -5. **[Observability](./observability)** - How we monitor and debug -6. **[Test Flows](./test-flows)** - How we test everything - -## Quick Reference - -### Service Ports (Docker Compose defaults) - -| Service | Port | Access Notes | -|---------|------|-------------| -| **Kong Gateway** | 8000 | Entry point for `/llm/*`, `/responses/*`, `/media/*`, `/mcp` (routing + auth) | -| **LLM API** | 8080 | Internal; exposed through Kong routes | -| **Response API** | 8082 | Internal; streaming SSE via Kong `/responses` | -| **Media API** | 8285 | Internal; proxied by Kong `/media` | -| **MCP Tools** | 8091 | Internal; routed through Kong `/mcp` | -| **Memory Tools** | 8090 | Internal; semantic memory service | -| **Realtime API** | 8186 | Internal; WebRTC session management | -| **Template API** | 8185 | Dev scaffold (not deployed by default) | -| **Keycloak** | 8085 | Admin console (protect behind VPN/SSO in production) | -| **vLLM** | 8101 | Inference backend (local GPU/CPU profile) | -| **Prometheus** | 9090 | Dev-only monitoring UI (`make monitor-up`) | -| **Jaeger** | 16686 | Trace UI | -| **Grafana** | 3331 | Dashboards (admin/admin in dev) | - -### Technology Stack - -| Component | Technology | -|-----------------|--------------------------------| -| API Gateway | Kong 3.5 + `keycloak-apikey` plugin | -| Services | Go 1.21+ (Gin framework, zerolog, wire DI) | -| MCP Server | mark3labs/mcp-go v0.7.0 | -| ORM | GORM + goose migrations | -| Database | PostgreSQL 15/16 (Docker) / managed service | -| Auth | Keycloak (OpenID Connect) | -| Inference | vLLM (OpenAI-compatible) or remote providers | -| Observability | OpenTelemetry Collector | -| Metrics | Prometheus 2.48 | -| Tracing | Jaeger 1.51 | -| Dashboards | Grafana 10.2 | - -## How to Run It - -You can run Jan Server in different ways: - -### Docker Compose (For Development) - -Use Docker Compose to run on your local computer: - -- `make quickstart` - interactive wizard (creates `.env`, starts stack) -- `make up-full` - bring up all services (`docker compose.yml` + `infra/docker/*.yml`) -- `make up-vllm-gpu` / `make up-vllm-cpu` - start vLLM profile -- `make monitor-up` - start Prometheus, Grafana, Jaeger -- `make down` / `make down-clean` - stop stack (preserve or remove volumes) - -### Kubernetes (For Production) - -Use Kubernetes to run in the cloud or on servers: - -- **Local testing**: Minikube or kind (see `k8s/SETUP.md`) -- **Production**: `k8s/jan-server` Helm chart + managed Postgres + managed Keycloak -- **Hybrid**: Run inference locally while other services run in the cluster - -**Helpful references:** -- [Kubernetes Setup Guide](../../k8s/SETUP) - minikube/bootstrap walkthrough -- [Kubernetes README](../../k8s/) - Helm values, ingress, TLS -- [Deployment Guide](../guides/deployment) - Docker, hybrid, CI/CD instructions - -## References - -- [System Design Details](./system-design) -- [Service Configurations](./services) -- [Data Flow Patterns](./data-flow) -- [Security Model](./security) -- [Observability Guide](./observability) -- [Test Flows & Diagrams](./test-flows) -- [API Reference](../api/) -- [Development Guide](../guides/development) - - - - diff --git a/apps/platform/content/docs/architecture/meta.json b/apps/platform/content/docs/architecture/meta.json deleted file mode 100644 index e7f849e0..00000000 --- a/apps/platform/content/docs/architecture/meta.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "title": "Architecture", - "pages": [ - "index", - "system-design", - "services", - "data-flow", - "security", - "security-advanced", - "observability", - "test-flows" - ] -} diff --git a/apps/platform/content/docs/architecture/observability.mdx b/apps/platform/content/docs/architecture/observability.mdx deleted file mode 100644 index 69110013..00000000 --- a/apps/platform/content/docs/architecture/observability.mdx +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: "Observability Guide" ---- - -# Observability Guide - -## Metrics -- **Prometheus** (http://localhost:9090) - - Scrapes Go services via `/metrics`. - - Includes default dashboards for request rate, latency, error ratio. -- **Service metrics**: - - LLM API: request duration, token usage, provider latency. - - Response API: tool execution counts, depth histogram, orchestration latency. - - Media API: upload size, S3 latency, resolution cache hits. - - MCP Tools: tool success/failure, backend latency. - -## Tracing -- **OpenTelemetry Collector** listens on `otel-collector:4317`. -- Services enable tracing by setting `OTEL_ENABLED=true` and `OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317`. -- **Jaeger** UI (http://localhost:16686): - - Search by `service.name`. - - Correlate request IDs (`X-Request-Id`) between services. - -## Logging -- All services use structured JSON logs (zerolog). -- Docker Compose aggregates stdout/stderr; use `make logs-` targets: - - `make logs-api`, `make logs-media-api`, `make logs-mcp`, `make logs-infra`. -- For Kubernetes, send logs to Loki or your aggregator via sidecars/DaemonSets. - -## Dashboards -- **Grafana** (http://localhost:3331, admin/admin by default). -- Import dashboards from `monitoring/grafana/provisioning/dashboards/`. -- Suggested panels: - - Request/response duration per service. - - Database connection pool usage. - - MCP tool success/error counts. - - Media upload throughput and storage utilisation. - -## Alerts -- Configure Alertmanager rules by creating `monitoring/alerting-rules.yml` based on `monitoring/prometheus-alerts.yml` as a reference. -- Recommended alerts: - - High error rate (>5% for 5 minutes) - - Slow LLM responses (>5s p95) - - Media API S3 failures - - MCP tool timeout spikes - -## Developer Workflow -1. Start the monitoring stack: `make monitor-up`. -2. Hit the APIs (curl/Postman/jan-cli api-test). -3. Inspect metrics/traces/logs using the URLs above. -4. Tear down with `make monitor-down` (if defined) or `docker compose down` for the monitoring profile. - -Update this file if ports or dashboards change. diff --git a/apps/platform/content/docs/architecture/security-advanced.mdx b/apps/platform/content/docs/architecture/security-advanced.mdx deleted file mode 100644 index 599c2910..00000000 --- a/apps/platform/content/docs/architecture/security-advanced.mdx +++ /dev/null @@ -1,237 +0,0 @@ ---- -title: "Security Architecture Deep Dive" ---- - -# Security Architecture Deep Dive - -> **Status:** v0.0.14 | **Last Updated:** December 23, 2025 | **Level:** Advanced - -Comprehensive security documentation covering authentication, authorization, encryption, threat models, and defense strategies. - -## Table of Contents - -- [Authentication Architecture](#authentication-architecture) -- [Authorization (RBAC)](#authorization-rbac) -- [Encryption](#encryption) -- [Threat Models & Mitigations](#threat-models--mitigations) -- [API Security](#api-security) -- [Multi-Tenant Isolation](#multi-tenant-isolation) -- [Audit Logging](#audit-logging) -- [Security Best Practices](#security-best-practices) - ---- - -## Authentication Architecture - -### Overview - -Jan Server uses a multi-layered authentication system supporting multiple authentication methods: OAuth2 and API Key authentication flow through an Authentication Gateway (Keycloak/OIDC), followed by JWT Validation and Token Introspection, then Authorization (RBAC/ABAC), and finally access to Protected Resources. - -### Keycloak/OIDC Integration - -#### Configuration - -Configure Keycloak with auth_server_url, realm, client_id, client_secret, and openid_config endpoints (issuer, authorization_endpoint, token_endpoint, userinfo_endpoint, jwks_uri). - -#### OAuth2 Code Flow with PKCE - -The OAuth2 flow with PKCE follows this sequence: User clicks login → Jan Server generates code_challenge and redirects to Keycloak /authorize → User submits credentials → Keycloak redirects to callback with authorization code → Jan Server exchanges code for JWT tokens (access_token, id_token, refresh_token) using code_verifier → Session cookie is set and user is redirected to dashboard. - -#### JWT Token Structure - -JWT tokens contain three parts: Header (algorithm RS256, type JWT, key ID), Payload (issuer, subject, audience, expiration, issued at, auth time, user details, roles and permissions), and Signature (RSASSA-PKCS1-v1_5 signature using private key). - -#### Token Validation Flow - -Token validation involves: fetching public keys from JWKS endpoint, decoding the JWT header to extract the key ID, retrieving the corresponding public key, verifying the signature using RS256 algorithm, validating issuer and audience claims, checking expiration, and returning the payload. Middleware validates tokens on each request. - -### Guest Login - -For unauthenticated users, Jan Server supports guest login via POST /llm/auth/guest-login endpoint. No credentials are required. Returns a temporary token (expires in 1 hour). Limited to read-only operations and rate limited to 10 requests per hour per IP. - ---- - -## Authorization (RBAC) - -### Role Hierarchy - -**Administrator**: All permissions, manage users and roles, access audit logs, system configuration. - -**Premium User**: Create conversations, use all models, webhooks, MCP tools, batch operations, priority support. - -**Standard User**: Create conversations, use free models, basic features, rate limited. - -**Guest**: Read-only access, no conversation creation, limited model access, heavily rate limited. - -### Permission Matrix - -Permissions vary by role: -- **Guest**: Can only GET /models -- **Standard**: Can GET/POST/PATCH conversations, GET models, LIMITED chat completions, upload 5 files -- **Premium**: All Standard permissions plus DELETE conversations, unlimited chat completions, upload 100 files, webhooks -- **Admin**: All permissions including admin endpoints - -### RBAC Implementation - -RBAC is implemented using decorators that check user roles and permissions from JWT tokens. Role-based decorators verify if a user has required roles (e.g., premium, admin). Permission-based decorators check specific resource:action permissions (e.g., write:webhooks). Both abort with 403 if requirements are not met. - ---- - -## Encryption - -### Data in Transit (TLS/SSL) - -**TLS 1.3 Configuration**: Uses cipher suites TLS_AES_256_GCM_SHA384 (preferred), TLS_CHACHA20_POLY1305_SHA256, and TLS_AES_128_GCM_SHA256. Certificates issued by DigiCert/Let's Encrypt with 1-year validity and auto-renewal 30 days before expiry. SAN covers *.jan.ai and jan.ai. Perfect Forward Secrecy enabled. HSTS configured with max-age=31536000 and includeSubDomains. - -#### Certificate Pinning - -Certificate pinning is implemented using custom HTTP adapters that create an SSL context and load specific server certificates for verification. This prevents man-in-the-middle attacks by ensuring only trusted certificates are accepted. - -### Data at Rest (Database Encryption) - -Database encryption at rest uses MySQL InnoDB encryption features: enable innodb_encrypt_tables and innodb_encrypt_log, configure keyring for key management, create tables with ENCRYPTION='Y' option for sensitive columns (title, content). Verify encryption status by querying INFORMATION_SCHEMA.TABLES. - -### Field-Level Encryption - -Field-level encryption uses Fernet symmetric encryption with a master key. Sensitive fields are encrypted before storage and decrypted when retrieved. The master key is stored securely in environment variables. - ---- - -## Threat Models & Mitigations - -### Threat Matrix - -**STRIDE Threat Model**: - -**S - Spoofing Identity**: Attacker impersonates legitimate user (Medium likelihood, High impact). Mitigations: Mutual TLS, MFA, IP whitelisting, hardware security keys. - -**T - Tampering with Data**: Attacker modifies data in transit/at rest (Low likelihood, Critical impact). Mitigations: TLS 1.3, database encryption, HMAC, cryptographic signatures, integrity verification. - -**R - Repudiation**: Attacker denies performing action (Medium likelihood, High impact). Mitigations: Comprehensive audit logging, non-repudiation tokens, immutable logs, 3rd-party verification. - -**I - Information Disclosure**: Attacker gains access to sensitive data (Medium likelihood, Critical impact). Mitigations: RBAC, encryption at rest and in transit, data masking, secrets management (Vault), DLP. - -**D - Denial of Service**: Attacker disrupts service availability (High likelihood, High impact). Mitigations: Rate limiting, DDoS protection (Cloudflare), load balancing, circuit breakers, auto-scaling. - -**E - Elevation of Privilege**: Attacker gains admin/higher access (Low likelihood, Critical impact). Mitigations: Principle of least privilege, RBAC, MFA, privilege separation, regular access reviews. - -### Attack Scenarios & Responses - -#### Scenario 1: Compromised API Token - -**Threat**: Attacker obtains user's API token and makes requests. - -**Detection**: Unusual API usage pattern, requests from unfamiliar IPs, rate limit exceeded, concurrent requests from multiple IPs. - -**Response**: Immediately revoke token, notify user via email, require password reset, enable MFA, review and audit recent actions, block suspicious IP addresses. - -#### Scenario 2: SQL Injection - -**Threat**: Attacker injects SQL through user input. - -**Prevention**: Use parameterized queries (prepared statements), input validation and sanitization, ORMs (SQLAlchemy), WAF (Web Application Firewall), regular security scanning. Always use parameterized queries or ORM methods instead of string concatenation. - -#### Scenario 3: Cross-Site Request Forgery (CSRF) - -**Threat**: Attacker tricks user into making unwanted request. - -**Prevention**: Use CSRF tokens on all state-changing operations, SameSite cookie attribute, verify Origin/Referer headers, user confirmation for sensitive operations. Generate unique CSRF tokens per session and validate them on all POST/PUT/PATCH/DELETE requests. - ---- - -## API Security - -### Rate Limiting Strategy - -**Tier 1 - Anonymous/Guest**: 10 requests/minute, 100 requests/hour per IP, burst 20 requests/10 seconds. - -**Tier 2 - Authenticated User**: 100 requests/minute, 10,000 requests/hour per user, burst 200 requests/10 seconds. - -**Tier 3 - Premium User**: 1,000 requests/minute, 1,000,000 requests/hour per user, burst 2,000 requests/10 seconds. - -**Tier 4 - API Key Holder**: Configurable per API key, can request custom limits, usage tracking and alerts. - -#### Implementation - -Rate limiting is implemented using decorators with Redis storage backend. The key function determines whether to rate limit by user ID (authenticated) or IP address (anonymous). Limits are specified per endpoint using decorator syntax. - -### Input Validation - -Input validation uses schema-based validation with Pydantic models. Define field constraints (min_length, max_length, regex patterns) and validate request data before processing. Return 400 errors with detailed validation messages on failure. - -### Output Encoding - -Prevent XSS attacks by HTML-encoding user input in responses. Use html.escape() for manual encoding or rely on template engine auto-escaping (Jinja2 auto-escapes by default). - ---- - -## Multi-Tenant Isolation - -### Data Isolation - -Each tenant has isolated data: users, conversations, media files, and settings. Database schema includes tenant_id in all tables (conversations, messages, media_files, audit_logs). All queries must include tenant_id filter to ensure data isolation. - -### Tenant Context - -Tenant context is extracted from JWT tokens and validated on every resource access. A TenantContext class provides methods to get the current tenant ID and validate that users can only access resources belonging to their tenant, aborting with 403 on violations. - -### Network Isolation - -Kubernetes NetworkPolicy enforces tenant isolation at the network level. Policies restrict ingress traffic to port 8000 from jan-server namespace only, and egress traffic to PostgreSQL (port 5432) within the same namespace. - ---- - -## Audit Logging - -### Audit Trail - -All sensitive operations are logged with an AuditLog class that captures: timestamp, event_type, user_id, tenant_id, resource, action, result (success/failure), ip_address, user_agent, and request_id. Log entries are immutably appended to the database and shipped to centralized logging. Every endpoint logs both successful and failed operations. - -### Audit Events - -Audit events include: LOGIN (user_id, ip, status), TOKEN_GENERATED/REVOKED (user_id, token details), PASSWORD_CHANGED (user_id, timestamp), PERMISSION_GRANTED/REVOKED (user_id, role, actor), CONVERSATION_CREATED/DELETED (user_id, conv_id), MEDIA_UPLOADED (user_id, file details), DATA_EXPORTED (user_id, data_type, count), ADMIN_CONFIG_CHANGED (admin_id, setting, values), FAILED_AUTH_ATTEMPT (ip, username, reason), SUSPICIOUS_ACTIVITY (user_id, activity details). - ---- - -## Security Best Practices - -### Development - -**Security Headers**: Set X-Content-Type-Options (nosniff), X-Frame-Options (DENY), X-XSS-Protection (1; mode=block), Strict-Transport-Security (max-age=31536000), Content-Security-Policy (default-src 'self') on all responses. - -**Secrets Management**: Load secrets from environment variables using dotenv, never hardcode credentials. - -**Dependency Scanning**: Use safety to check for vulnerable dependencies. - -**Code Scanning**: Use bandit for Python security linting. - -**SAST**: Use tools like SonarQube and Snyk for static analysis. - -**Regular Updates**: Keep dependencies up to date with pip upgrade. - -### Deployment - -**Runtime Security**: Configure pod securityContext with runAsNonRoot: true, runAsUser: 1000, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false, drop all capabilities. - -**Secrets Handling**: Load secrets from Kubernetes secretKeyRef instead of environment variables. - -**RBAC**: Define minimal Kubernetes roles that only allow access to specific secrets with get verb. - -### Monitoring - -**Security Monitoring Checklist**: Failed login attempts, brute force detection, unusual API patterns, permission escalation attempts, unencrypted data transmission, certificate expiration, rate limit violations, unauthorized access attempts, configuration changes, audit log tampering, DLP alerts, intrusion detection. - ---- - -## See Also - -- [Architecture Overview](./) -- [Performance & SLA Guide](./performance) - -- [Webhooks Guide](../guides/webhooks) - ---- - -**Generated:** December 23, 2025 -**Status:** Production-Ready -**Version:** v0.0.14 diff --git a/apps/platform/content/docs/architecture/security.mdx b/apps/platform/content/docs/architecture/security.mdx deleted file mode 100644 index f1e99752..00000000 --- a/apps/platform/content/docs/architecture/security.mdx +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: "Security Architecture" ---- - -# Security Architecture - -## Identity and Access -- **OAuth2/OIDC** via Keycloak (`keycloak/` Dockerfile). -- **Kong gateway** (`http://localhost:8000`) protects every `/llm/*` route using the built-in `jwt` plugin (validating Keycloak tokens) plus the custom `keycloak-apikey` plugin (`X-API-Key: sk_*` -> `POST /auth/validate-api-key`). -- **Clients** obtain tokens using: - - Guest endpoint (`POST /llm/auth/guest-login` via Kong) for quick local access; the LLM API coordinates with Keycloak. - - OAuth2 (code/password/device) flows against the `jan` realm in Keycloak for registered users. -- **Services** validate tokens with: - - `AUTH_ENABLED=true` - - `AUTH_ISSUER`, `ACCOUNT`, `AUTH_JWKS_URL` -- **Service auth**: Media API, Response API, and MCP Tools enforce Keycloak-issued JWTs via `AUTH_*` settings and inherit Kong headers when needed. -- **Kong plugins**: besides jwt/apikey, Kong applies rate limiting, request size limits, and header sanitization at the edge to keep unauthenticated traffic out. - -## Network Boundaries -- **Public**: Kong (8000) and, optionally, Keycloak admin (8085) when protected. -- **Private**: LLM API (8080), Response API (8082), Media API (8285), MCP Tools (8091), vLLM (8101). -- **MCP network**: SearXNG, Redis, Vector Store, SandboxFusion run on `jan-server_mcp-network` and are not exposed externally. -- **Kubernetes**: use NetworkPolicies to isolate namespaces or rely on service mesh if available. - -## Data Protection -- **Databases**: PostgreSQL instances run inside Docker/Kubernetes. Use managed services with TLS for production. -- **S3 credentials**: stored in `.env` or secret stores, mounted into Media API only. -- **jan_* identifiers**: act as opaque references; actual S3 URLs are short lived. -- **Logs**: structured JSON, avoid logging secrets (token middleware redacts sensitive headers). - -## Secrets Lifecycle -1. Add new variables to `.env.template` with clear comments. -2. Mirror them in `config/secrets.env.example`. -3. Document usage in `config/README.md` and relevant service README. -4. For production, load values from secret managers or Kubernetes secrets instead of `.env`. - -## Threat Mitigations -- **JWT validation**: services reject expired or mismatched tokens and refresh their JWKS cache periodically. -- **Tool execution**: SandboxFusion isolates python code; `SANDBOX_FUSION_REQUIRE_APPROVAL` can force manual approval. -- **Web fetches**: SearXNG provides result filtering; Response API enforces depth/time budgets. -- **Media uploads**: requests require a Bearer token plus `MEDIA_MAX_BYTES`/content-type validation before accepting bytes. -- **Rate limits**: configure Kong plugins per route; Response API also throttles multi-step workflows internally. - -## Incident Response -- Capture request IDs from response headers to trace calls across services. -- Use Jaeger + Prometheus dashboards for triage. diff --git a/apps/platform/content/docs/architecture/services.mdx b/apps/platform/content/docs/architecture/services.mdx deleted file mode 100644 index 729231bf..00000000 --- a/apps/platform/content/docs/architecture/services.mdx +++ /dev/null @@ -1,92 +0,0 @@ ---- -title: "Service Overview" ---- - -# Service Overview - -Jan Server ships four core services plus shared infrastructure. Use this document to understand how they fit together and where to look in the codebase. - -## Core Services - -| Service | Purpose | Port(s) | Source | Primary Docs | -|---------|---------|---------|--------|--------------| -| **LLM API** | OpenAI-compatible chat completions, conversation storage, model management | 8080 (direct), 8000 via Kong | `services/llm-api` | [../api/llm-api/README.md](../api/llm-api/) | -| **Response API** | Multi-step orchestration, tool chaining, integration with MCP Tools | 8082 | `services/response-api` | [../api/response-api/README.md](../api/response-api/) | -| **Media API** | Binary ingestion, jan_* IDs, S3 storage and resolution | 8285 | `services/media-api` | [../api/media-api/README.md](../api/media-api/) | -| **MCP Tools** | Model Context Protocol tools (web search, scraping, file search, python exec) | 8091 | `services/mcp-tools` | [../api/mcp-tools/README.md](../api/mcp-tools/) | -| **Memory Tools** | Semantic memory with BGE-M3 embeddings, conversation caching | 8090 | `services/memory-tools` | `services/memory-tools/README.md` | -| **Realtime API** | WebRTC session management via LiveKit for real-time audio/video | 8186 | `services/realtime-api` | `services/realtime-api/README.md` | -| **Template API (scaffold)** | Go microservice blueprint for new services (not deployed) | 8185 | `services/template-api` | [../guides/services-template.md](../guides/services-template) | - -## Configuration - -All services use the **centralized configuration system** at `pkg/config/`: - -- **Type-safe:** Go structs with compile-time validation -- **YAML defaults:** `config/defaults.yaml` for base configuration -- **Environment overrides:** Service-specific env vars (e.g., `LLM_API_HTTP_PORT`) -- **Kubernetes values:** Auto-generated from configuration structs -- **CLI tool:** `jan-cli config` for validation and inspection - -See [Configuration Documentation](../configuration/) for details. - -## Infrastructure Components - -For detailed infrastructure architecture (Kong, Keycloak, databases, observability), see [System Design](system-design#2-architecture-layers). - -## Creating a New Service - -### Quick Start - -```bash -# Generate from template -scripts/new-service-from-template.ps1 -Name my-service -``` - -### Configuration Setup - -New services should use the centralized configuration system: - -1. **Define service config in `pkg/config/types.go`:** - ```go - type ServiceConfig struct { - HTTP HTTPConfig `yaml:"http"` - Database DatabaseConfig `yaml:"database"` - // Add service-specific fields - } - ``` - -2. **Regenerate config files:** - ```bash - make config-generate - ``` - -3. **Load config in your service:** - ```go - import "jan-server/pkg/config" - - cfg, _:= config.Load() - serviceCfg, _:= cfg.GetServiceConfig("my-service") - ``` - -4. **Update deployment configs:** - - Add service to `docker/services-api.yml` - - Generate K8s values: `jan-cli config k8s-values --env production` - -See [Configuration System](../configuration/) and [Service Template Guide](../guides/services-template) for the scaffold and full instructions. - -### Documentation Requirements - -1. Update `docs/architecture/services.md` (this file) with new service row -2. Create `docs/api//README.md` with API reference -3. Add service to `docs/README.md` navigation -4. Update `k8s/jan-server/values.yaml` if deploying to Kubernetes - -## Service Interactions -- **LLM API -> Media API**: LLM API resolves `jan_*` IDs before sending payloads to vLLM or upstream providers (`MEDIA_RESOLVE_URL` env var). -- **Response API -> LLM API**: Response API delegates final language generation to LLM API (`LLM_API_URL`). -- **Response API -> MCP Tools**: orchestrated tool calls are issued via JSON-RPC (`MCP_TOOLS_URL`). -- **MCP Tools -> Infrastructure**: uses SearXNG, Vector Store, and SandboxFusion to execute user requests. - -Keep this document updated whenever a service is added, renamed, or retires. - diff --git a/apps/platform/content/docs/architecture/system-design.mdx b/apps/platform/content/docs/architecture/system-design.mdx deleted file mode 100644 index 3b1aeac2..00000000 --- a/apps/platform/content/docs/architecture/system-design.mdx +++ /dev/null @@ -1,131 +0,0 @@ ---- -title: "System Design" ---- - -# System Design - -This reference describes how the Jan Server components fit together. Use it when reviewing cross-service changes or planning deployments. - -## 1. System Overview - -Jan Server is a microservices platform that exposes OpenAI-compatible APIs through Kong. Each service owns a focused domain: - -- **LLM API (8080)** - chat completions, conversations, projects, model catalog. -- **Response API (8082)** - multi-step orchestration and MCP tool coordination. -- **Media API (8285)** - binary ingestion, jan_* IDs, presigned URL management. -- **MCP Tools (8091)** - JSON-RPC endpoint that proxies Serper/SearXNG search, scraping, file search, and SandboxFusion execution. -- **Memory Tools (8090)** - semantic memory using BGE-M3 embeddings with caching and batch processing. -- **Realtime API (8186)** - WebRTC session management via LiveKit for real-time audio/video communication. -- **Shared infrastructure** - Kong (8000), Keycloak (8085), PostgreSQL, vLLM (8101), observability stack. - -Note: Template API (8185) is a development scaffold and not part of the deployed stack. - -Kong terminates TLS (in production), validates JWT/API keys, applies rate limits, and forwards requests to the internal services. - -## 2. Architecture Layers - -| Layer | Components | Notes | -|-------|------------|-------| -| **Edge** | Kong Gateway, Keycloak | Centralized auth, rate limiting, guest-token endpoint. | -| **Application** | LLM API, Response API, Media API, MCP Tools, Memory Tools, Realtime API | Written in Go using Gin + zerolog, configured via `pkg/config`. | -| **Tooling** | SearXNG, Serper, SandboxFusion, vector-store | Only accessible from MCP Tools. | -| **Data/Storage** | PostgreSQL (`api-db`, `keycloak-db`), S3-compatible storage | Media files live in object storage; metadata lives in PostgreSQL. | -| **Inference** | vLLM (local) or remote OpenAI-compatible providers | Selected per request using the provider metadata catalog. | -| **Observability** | OpenTelemetry Collector, Prometheus, Grafana, Jaeger | Enabled with `OTEL_ENABLED=true` + `make monitor-up`. | - -## 3. Component Diagram - -``` - +------------------------------+ - | External Clients / SDKs | - +---------------+--------------+ - | - v - +-------------------+ - | Kong Gateway | 8000 - +---+---+----+------+ - | | | - +--------------+ | +----------------+ - | | | - v v v - +-----------+ +---------------+ +---------------+ - | LLM API | | Response API | | Media API | - | (8080) | | (8082) | | (8285) | - +-----+-----+ +-------+-------+ +-------+-------+ - | | | - | v | - | +-------------------+ | - +------->| MCP Tools |<----------+ - | | (8091) | - | +----+---+----+-----+ - | | | | - | | | +--> SandboxFusion - | | +-------> Vector Store - | +-----------> SearXNG / Serper - | - v - +---------------+ +----------------+ - | Memory Tools | | Realtime API | - | (8090) | | (8186) | - +---------------+ +----------------+ - -Shared dependencies (not shown): PostgreSQL (api-db), S3/Object storage, Keycloak (JWT issuer), vLLM (8101), BGE-M3 (embeddings), LiveKit (WebRTC). -``` - -## 4. Request Lifecycles - -### Chat Completions -1. Client calls `POST /v1/chat/completions` on `http://localhost:8000`. -2. Kong validates the JWT/API key and forwards to `llm-api:8080`. -3. LLM API resolves `jan_*` placeholders via Media API, selects a provider (local vLLM or remote), and streams tokens back to the gateway. -4. Conversations/projects are persisted in PostgreSQL. - -### Response Orchestration -1. Client calls `POST /responses/v1/responses` (streaming optional). -2. Response API loads the conversation context and iteratively issues `tools/list` / `tools/call` requests to MCP Tools. -3. Tool executions are capped by `RESPONSE_MAX_TOOL_DEPTH` and `TOOL_EXECUTION_TIMEOUT`. -4. Final synthesis is delegated to LLM API and streamed back to the caller. - -### Media Handling -1. Upload via `POST /media/v1/media` (remote URL or data URL) or request a presigned upload with `POST /media/v1/media/prepare-upload`. -2. Media API deduplicates content, issues a `jan_*` ID, and stores metadata in PostgreSQL. -3. Other services embed the `jan_*` ID; LLM API resolves them to presigned URLs right before inference. - -### MCP JSON-RPC -1. Response API or external automation sends JSON-RPC requests to `POST /v1/mcp`. -2. MCP Tools validates the method (`tools/list`, `tools/call`, `prompts/*`, `resources/*`) and dispatches to the Serper/SearXNG/SandboxFusion clients. -3. Results are returned as SSE events (streaming) or plain JSON when the response fits a single chunk. - -## 5. Data & Network Topology - -- Docker Compose defines two primary networks: `jan-server_default` (Kong + core services + databases) and `jan-server_mcp-network` (MCP-only helpers such as SearXNG, vector store, SandboxFusion). -- Production deployments should mirror this split using Kubernetes namespaces or NetworkPolicies. -- Persistent data: - - `api-db` (LLM/Response/Media metadata) - each service uses its own schema. - - `keycloak-db` - Keycloak realm and client configuration. - - Object storage (S3, MinIO, etc.) - Media files and presigned URLs. - -## 6. Deployment Modes - -| Mode | Description | Commands | -|------|-------------|----------| -| **Local (recommended)** | `make quickstart` prompts for providers, writes `.env`, and runs `docker compose up` with all services. | `make quickstart` | -| **Profiles** | Start a subset of services (API only, MCP only, GPU inference). | `make up-api`, `make up-mcp`, `make up-gpu` | -| **Monitoring stack** | Optional Prometheus/Grafana/Jaeger. | `make monitor-up` | -| **Kubernetes** | Use `k8s/jan-server` Helm chart. Values mirror `pkg/config` defaults. | `helm install jan ./k8s/jan-server -f values.yaml` | - -## 7. Change Impact Checklist - -When modifying the system architecture: -1. Update the relevant service README and API docs. -2. Reflect new ports/paths in Kong configuration. -3. Adjust `docs/architecture/services.md` and `docs/architecture/data-flow.md`. -4. Regenerate configuration artifacts (`make config-generate`) if `pkg/config` changes. -5. Update Kubernetes values and Helm defaults as needed. - ---- - -**Maintainer:** Jan Server Architecture Group - **Last Reviewed:** November 2025 - - - diff --git a/apps/platform/content/docs/architecture/test-flows.mdx b/apps/platform/content/docs/architecture/test-flows.mdx deleted file mode 100644 index 3ecfe38c..00000000 --- a/apps/platform/content/docs/architecture/test-flows.mdx +++ /dev/null @@ -1,679 +0,0 @@ ---- -title: "Test Flows Architecture & Diagrams" ---- - -# Test Flows Architecture & Diagrams - -**Generated**: December 2025 - -This document provides visual representations of test flows, dependencies, and service interactions across the jan-server test suite. See [System Design](./system-design) for the complete system architecture. - ---- - -## Overview - -The jan-server test suite consists of Postman collections with 100+ individual test cases covering: -- Authentication flows (guest, JWT, API keys) -- Conversation and project management -- Model catalog and prompt templates -- Tool orchestration via MCP (admin and runtime) -- Media file operations -- Response generation with tool calling -- User management - -All tests follow dependency chains: Health -> Auth -> Setup -> Main Tests -> Cleanup - -For complete system architecture diagrams, see [System Design](./system-design). - ---- - -## Test Collections Overview - -### 1. Auth Tests (`auth.postman.json`) - -**Focus**: Authentication flows and token management - -``` -Health Checks - v -Setup [Guest Token] -> [Keycloak Admin] -> [Create User] -> [Set Password] - v -Main Tests (Parallel) -+- Guest Login Flow [Request Token, Upgrade Account] -+- JWT Login Flow [Keycloak Auth, User Management] -+- API Key Flow [Create, List, Use, Revoke] - v -Cleanup [Delete User] -``` - -**20+ Test Cases** - ---- - -### 2. Conversation Tests (`conversation.postman.json`) - -**Focus**: Conversation and project management - -``` -Health & Auth Setup - v -Model Discovery [List Available Models] - v -Project Management (Parallel) -+- Create Projects (3 types) -+- CRUD Operations -+- List & Pagination -+- Update (Name, Instructions, Favorite, Archive) -+- Validation Tests - v -Conversation Flow -+- Create Conversation -+- Verify Title -+- Start Chat (First Message) -+- Continue Chat (Follow-ups) -+- Get Details -+- List Conversations - v -Cleanup [Delete All Resources] -``` - -**30+ Test Cases** - ---- - -### 3. Model Tests (`model.postman.json`) - -**Focus**: Model catalog operations - -``` -Authentication - v -Model Catalog Operations -+- List Models -+- Get Model Details -+- Model Metadata -``` - -**Test Cases** - ---- - -### 4. Model Prompt Templates Tests (`model-prompt-templates.postman.json`) - -**Focus**: Prompt template management - -``` -Authentication - v -Template Operations -+- List Templates -+- Get Template -+- Create/Update Templates -``` - -**Test Cases** - ---- - -### 5. MCP Admin Tests (`mcp-admin.postman.json`) - -**Focus**: MCP administrative operations - -``` -Authentication - v -MCP Configuration -+- List Available Tools -+- Tool Configuration -+- Admin Settings -``` - -**Test Cases** - ---- - -### 6. MCP Runtime Tests (`mcp-runtime.postman.json`) - -**Focus**: MCP tool execution at runtime - -``` -Guest Authentication - v -Tool Discovery [List Available Tools] - v -Individual Tool Tests -+- Serper Search [Query with domain filters] -+- Web Scraping [Scrape URLs] -+- File Search Index [Index & Query documents] -+- Python Execution [Sandboxed code execution] -+- SearXNG Direct [Meta-search integration] -``` - -**8+ Test Cases** - ---- - -### 7. Media API Tests (`media.postman.json`) - -**Focus**: File upload, storage, and resolution - -``` -Authentication - v -Upload Operations -+- Presigned URL Generation -+- Remote URL Ingestion -+- Data URL Ingestion -+- Deduplication Testing - v -Resolution & Download -+- Payload Resolution (with jan_* placeholders) -+- Direct Stream Download -+- Error Cases (404, 400, 401) -``` - -**11 Test Cases** - ---- - -### 8. Response API Tests (`response.postman.json`) - -**Focus**: Response generation with tool orchestration - -``` -Authentication & Setup - v -Health & Service Checks -+- Response API Health -+- MCP Tools Availability -+- LLM API Smoke Test - v -Response Generation (Parallel) -+- Basic Text Responses [No tools] -+- Single Tool Calling [Search integration] -+- Multi-Step Tool Chains [Search + Scrape] -+- File Search Workflows [Index + Query] -+- Conversation Continuity [Multi-turn with context] -+- Error Handling [Invalid tools, missing params] -+- Complex Scenarios [Search + Scrape + Analyze] -``` - -**25+ Test Cases** - ---- - -### 9. User Management Tests (`user-management.postman.json`) - -**Focus**: User administration and account management - -``` -Authentication - v -User Operations -+- Create Users -+- List Users -+- Update User Details -+- Delete Users -+- Role Management -``` - -**Test Cases** - ---- - -### 6. Full Regression (`test-all.postman.json`) - -**Focus**: Executes all other collections sequentially for CI/regression. - -``` -Bootstrap (Health + Auth) - v -[Auth Collection] - v -[Conversations Collection] - v -[Media Collection] - v -[MCP Tools Collection] - v -[Response API Collection] - v -Cleanup + Report -``` - -**1 Flow, 100+ Assertions** - -Use this collection when running `make test-all` or in CI pipelines-it reuses the shared environment file and preserves the dependency order shown above. - ---- - -## Test Flow Sequence Diagrams - -### Authentication Sequence - -``` -Test Runner Services - | | - +--> Health Check -> LLM API - <-- 200 OK <--+ - | | - +--> Guest Login -> /auth/guest-login - <-- {access_token,...} <--+ - | | - +--> Get Keycloak Token -> Keycloak - <-- {admin_token} <--+ - | | - +--> Create User -> /admin/realms/{realm}/users - <-- 201 Created <--+ - | | - +--> Set Password -> /admin/realms/{realm}/users/{id} - <-- 204 No Content <--+ - | | - +--> Obtain User Token -> /realms/{realm}/token - <-- {user_token} <--+ - | | - +--> Main Tests -> [Services] -``` - ---- - -### Conversation Flow - -``` -Test Runner Services - | | - +--> Authenticate -> LLM API - <-- {access_token} <--+ - | | - +--> List Models -> /v1/models - <-- [{model},...] <--+ - | | - +--> Create Project -> /v1/projects - <-- {project_id} <--+ - | | - +--> Create Conversation -> /v1/conversations - <-- {conversation_id} <--+ - | | - +--> Start Chat -> /v1/chat/completions - | (with conversation) (with conversation.id) - <-- {message, choices} <--+ - | | - +--> Continue Chat -> /v1/chat/completions - | (with history) (with prior messages) - <-- {message, choices} <--+ - | | - +--> Cleanup -> [Delete Resources] -``` - ---- - -### Response API with Tool Calling - -``` -Test Runner Services - | | - +--> Health Checks -> Response API, MCP Tools - <-- OK <--+ - | | - +--> Create Response -> Response API - | (with tool config) /responses (POST) - <-- {response_id} <--+ - | | - | Response Service Calls Tools (Internal) - | +-> MCP Tools - | | /tools/call (search) - | | <- {results} - | | - | +-> MCP Tools - | | /tools/call (scrape) - | | <- {content} - | | - | +-> LLM API - | /v1/chat/completions - | <- {final_response} - | - +--> Get Response -> /responses/{id} - <-- {id, content,...} <--+ - | | - +--> Verify Results OK Success -``` - ---- - -### Media Processing Flow - -``` -Test Runner Services - | | - +--> Authenticate -> LLM API - <-- {access_token} <--+ - | | - +--> Get Presigned URL -> Media API - +--> /media/presign (Client uploads to S3/Object Storage) - <-- {presigned_url} <--+ - | | - +--> Ingest from URL -> Media API - +--> /media/ingest (source=url) - <-- {media_id, hash} <--+ - | | - +--> Test Deduplication -> Media API - +--> /media/ingest (same url) - <-- {media_id: same, deduped: true} <--+ - | | - +--> Resolve Placeholder -> Media API - +--> /media/resolve ({{jan_media_{id}}}) - <-- {content: resolved_url} <--+ - | | - +--> Download -> Media API - /media/{id} (Stream binary data) -``` - ---- - -### MCP Tools Workflow - -``` -Test Runner Services - | | - +--> List Tools -> MCP Tools - | /tools/list - <-- {tools: [...]} <--+ - | | - +--> Execute Serper -> MCP Tools - | /tools/call (search) (calls Serper API) - <-- {results: [...]} <--+ - | | - +--> Execute Scrape -> MCP Tools - | /tools/call (scrape) (fetches URL content) - <-- {content} <--+ - | | - +--> Index Documents -> MCP Tools - | /tools/call (index) (builds search index) - <-- {indexed_id, chunks} <--+ - | | - +--> Query Index -> MCP Tools - | /tools/call (query) (searches index) - <-- {results: [...]} <--+ - | | - +--> Execute Python -> MCP Tools - /tools/call (exec) (sandboxed execution) -``` - ---- - -## Test Dependency Matrix - -``` -+----------------------------------------------------------------+ -| TEST DEPENDENCY HIERARCHY | -+----------------------------------------------------------------+ - -Level 0: Health Checks -+- Verify all services are running - -Level 1: Authentication -+- Guest Token Generation -+- Keycloak Integration -+- JWT Token Generation -+- API Key Management - -Level 2: Resource Discovery & Setup -+- List Available Models -+- Create Projects -+- Create Conversations -+- Initialize Test Data - -Level 3: Functional Tests (Can run in parallel) -+- Conversation Operations -+- Chat Completions -+- Tool Calling & Orchestration -+- Media File Operations -+- Response Generation - -Level 4: Integration Tests -+- Multi-step Workflows -+- Cross-service Interactions -+- Conversation Continuity -+- Tool Chaining - -Level 5: Cleanup -+- Delete All Test Resources -``` - ---- - -## Service Communication Map - -``` - +----------------------+ - | TEST RUNNER | - | (jan-cli api-test) | - +----------------------+ - | - +---------------+---------------+ - | | | - v v v - +------------+ +----------+ +----------+ - | Kong | |Keycloak | |SearXNG | - | (Gateway) | |(Auth) | |(Search) | - +------+-----+ +----+-----+ +----+-----+ - | | | - +----------+--------------+--------------+ - | | | - v v v -+----------+ +----------+ +------------+ -| LLM API |<--> MCP Tools | (external) | -|:8080 | |:8091 | | -+----+-----+ +-----+----+ +------------+ - | | - | +--------+ - | v - +-> Media API:8081 - | - +-> Response API:8082 - | - +-> PostgreSQL (persistent storage) -``` - ---- - -## Test Data Flow - -``` -+-----------------------------------------------------------------+ -| POSTMAN COLLECTION VARIABLES | -| | -| SETUP PHASE -| +- guest_access_token <- /auth/guest-login -| +- test_user_id <- /admin/realms/jan/users -| +- kc_admin_access_token <- /realms/master/token -| +- model_id <- /v1/models (first item) -| | -| LLM API PHASE -| +- project_id_1,2,3 <- /v1/projects (POST) -| +- conversation_id <- /v1/conversations (POST) -| +- conversation_title <- GET /v1/conversations/{id} -| | -| RESPONSE API PHASE -| +- response_id <- /responses (POST with tools) -| +- tool_result <- MCP /tools/call -| +- response_content <- GET /responses/{id} -| | -| MEDIA API PHASE -| +- presigned_url <- /media/presign -| +- media_id <- /media/ingest -| +- resolved_content <- /media/resolve -| | -| CLEANUP PHASE -| +- DELETE /v1/conversations/{id} -| +- DELETE /v1/projects/{id} -| +- DELETE /users/{test_user_id} -| | -+-----------------------------------------------------------------+ -``` - ---- - -## Error Handling Architecture - -``` -+------------------------+--------------+-------------------------+ -| Error Scenario | HTTP Status | Test Assertion | -+------------------------+--------------+-------------------------+ -| Service Unavailable | 503 | Retry or Fail Fast | -| Invalid Token | 401 | Verify Rejection | -| Insufficient Perms | 403 | Verify Denial | -| Resource Not Found | 404 | Expected for cleanup | -| Invalid Input | 400 or 422 | Validate Error Message | -| Resource Conflict | 409 | Handle Duplicate Create | -| Rate Limited | 429 | Implement Backoff | -| Internal Error | 500 | Retry or Report | -| Timeout | - | Extend Timeout | -| Connection Refused | - | Ensure Service Running | -+------------------------+--------------+-------------------------+ -``` - ---- - -## Workflow State Machine - -``` - START - | - v -+--------------+ -|Health Check |--NO---> FAIL (Service Down) -+------+-------+ - |YES - v -+------------------+ -|Authenticate |--NO---> FAIL (Auth Error) -|(Get Token) | -+------+-----------+ - |YES - v -+------------------+ -|Setup Resources |--NO---> FAIL (Setup Error) -|(Projects, Docs) | -+------+-----------+ - |YES - v -+------------------------------------------+ -|Execute Main Tests (Parallel/Serial) | -|- Conversations | -|- Tool Calls | -|- Media Operations | -|- Error Scenarios | -+------+-----------------------------------+ - | - +----+------+ - |YES NO | - v v -+--------+ +--------+ -|CLEANUP | |CLEANUP | -|SUCCESS | |& FAIL | -| Tests | | Tests | -+----+---+ +----+---+ - | | - +----+-----+ - v -+------------------+ -|Generate Report | -|- Pass/Fail | -|- Assertions | -|- Timing | -|- Coverage | -+------+-----------+ - v - +-----+ - | END | - +-----+ -``` - ---- - -## Test Execution Timeline - -``` -Timeline: 0s 5s 10s 15s 20s - -Auth Flow: [Health ]->[Auth ]->[Models ]->[Setup ] - v -Conversations: +-----------------[Project Mgmt ] - | v - | [Conversation Create->Get->Chat->Continue ] - v -Media API: | [Ingest->Dedup->Resolve->Download ] - | v -Response API: | [Basic Response][Tool Call][Multi-step] - | v -MCP Tools: | [Search][Scrape][IndexQuery][Python] - | -Cleanup: +---------------[Delete Resources] - -Total: ~15-25 seconds (depends on service latency) -``` - ---- - -## Test Coverage Summary - -| Component | Tests | Coverage | -|-----------|-------|----------| -| Authentication | 8 | Guest, JWT, API Keys | -| Conversations | 14 | CRUD, Pagination, Validation | -| Projects | 8 | CRUD, State Management | -| Chat Completion | 3 | Basic Usage, Conversation | -| Models | 2 | Listing, Details | -| Tool Calling | 8 | Search, Scrape, Index, Exec | -| Media Upload | 3 | URL, DataURL, Dedup | -| Media Download | 2 | Streaming, Error Cases | -| Error Handling | 5 | Invalid Input, Missing Auth | -| **TOTAL** | **100+** | **Comprehensive (see `test-all.postman.json`)** | - ---- - -## Integration Points - -### With System Design -See [System Design](./system-design) for: -- Architecture layers -- Service responsibilities -- Data flow patterns -- Deployment strategies - -### With Services -See [Services](./services) for: -- LLM API details -- Response API details -- Media API details -- MCP Tools details - -### With Security -See [Security](./security) for: -- Authentication mechanisms -- Authorization patterns -- API key management -- Token validation - -### With Data Flow -See [Data Flow](./data-flow) for: -- Request/response patterns -- Data transformation -- Persistence strategies - ---- - -## Related Documentation - -- **Main Architecture Index**: See `/docs/architecture/README.md` - ---- - -**Last Updated**: November 11, 2025 -**Document Type**: Architecture Reference - Testing -**Target Audience**: QA Engineers, Developers, DevOps -**Maintainer**: Jan-Server Team - - - diff --git a/apps/platform/content/docs/configuration/docker-compose.mdx b/apps/platform/content/docs/configuration/docker-compose.mdx deleted file mode 100644 index f854d681..00000000 --- a/apps/platform/content/docs/configuration/docker-compose.mdx +++ /dev/null @@ -1,86 +0,0 @@ ---- -title: "Docker Compose Generation" ---- - -# Docker Compose Generation - -## Overview - -This document describes how docker-compose files are managed in relation to the configuration system. - -## Current Approach - -Instead of generating docker-compose files from YAML config, we maintain the compose files directly with references to the config system: - -1. **Infrastructure** (`infra/docker/infrastructure.yml`) - PostgreSQL, Keycloak, Kong, shared networks/volumes. -2. **API Services** (`infra/docker/services-api.yml`) - llm-api, media-api, response-api. -3. **MCP Services** (`infra/docker/services-mcp.yml`) - mcp-tools, vector-store, sandbox helpers. -4. **Observability** (`infra/docker/observability.yml`) - Prometheus, Grafana, Jaeger, OTEL collector. -5. **Inference** (`infra/docker/inference.yml`) - vLLM GPU/CPU profiles. -6. **Development overlay** (`infra/docker/dev-full.yml`) - adds `host.docker.internal` mapping for hybrid workflows. - -The root `docker-compose.yml` stitches the profiles together (infrastructure + services + MCP + observability). Profiles such as `full`, `mcp`, `monitor`, and `dev-full` map directly to the files above. - -## Configuration Integration - -Each service in docker-compose references the standardized environment variables. Most values are pulled from `.env` (generated by `make quickstart`) and fall back to the defaults defined in `config/defaults.yaml` and `pkg/config/types.go`: - -```yaml -services: - llm-api: - environment: - # Database - constructed DSN from config defaults - DB_POSTGRESQL_WRITE_DSN: "postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@api-db:5432/${POSTGRES_DB}?sslmode=disable" - - # All other variables reference config/defaults.yaml structure - HTTP_PORT: ${HTTP_PORT:-8080} - LOG_LEVEL: ${LOG_LEVEL:-info} -``` - -## Why Direct Maintenance? - -1. **Simplicity** - Docker Compose is already declarative and easy to read -2. **Flexibility** - Allows docker-specific optimizations (healthchecks, extra hosts, bind mounts) -3. **Version Control** - Changes are clearly visible in git diffs -4. **No Generation Overhead** - No build step required -5. **Profiles** - Different dev/prod/monitoring stacks can be launched with a single `make` target - -## Future: Optional Generation - -If needed, a generator can be built using `pkg/config/compose/generator.go` that: -- Reads `config/defaults.yaml` -- Applies environment overrides -- Generates docker-compose YAML files -- Validates output - -## Validation - -To validate compose files: - -```bash -# Validate syntax -docker compose -f docker/infrastructure.yml config - -# Validate with current environment -docker compose -f docker-compose.yml config - -# Dry-run full stack -docker compose --profile full config - -# Verify dev overlay -docker compose --profile dev-full config -``` - -Typical networks and volumes: -- Networks: `jan-server_default` (core), `jan-server_mcp-network` (MCP helpers) -- Volumes: `api-db-data`, `keycloak-db-data`, `vector-store-data`, `grafana-data` - -All of the above are declared in the compose snippets so they can be inspected with `docker compose config`. - -## Sprint 4 Status - -OK **COMPLETE** - Docker compose files are consistent with config system -OK **COMPLETE** - All services use standardized environment variables -OK **COMPLETE** - Validation process documented - -**Rationale:** Direct maintenance is simpler and more maintainable than generation for this use case. The generator infrastructure exists in `pkg/config/compose/` if needed in the future. diff --git a/apps/platform/content/docs/configuration/env-var-mapping.mdx b/apps/platform/content/docs/configuration/env-var-mapping.mdx deleted file mode 100644 index 2ec6ee19..00000000 --- a/apps/platform/content/docs/configuration/env-var-mapping.mdx +++ /dev/null @@ -1,310 +0,0 @@ ---- -title: "Environment Variable Mapping" ---- - -# Environment Variable Mapping - -This document maps centralized configuration (`pkg/config/types.go`) environment variables to service-specific variables, facilitating the Sprint 3 migration. - -## Infrastructure - -### Database (PostgreSQL) - -| Centralized Env Var | Type | Default | Services Using | Notes | -|---------------------|------|---------|----------------|-------| -| `POSTGRES_HOST` | string | `api-db` | llm-api | Used in `DB_POSTGRESQL_WRITE_DSN` | -| `POSTGRES_PORT` | int | `5432` | llm-api | Used in `DB_POSTGRESQL_WRITE_DSN` | -| `POSTGRES_USER` | string | `jan_user` | llm-api | Used in `DB_POSTGRESQL_WRITE_DSN` | -| `POSTGRES_PASSWORD` | string | `jan_password` | llm-api | From secrets, used in `DB_POSTGRESQL_WRITE_DSN` | -| `POSTGRES_DB` | string | `jan_llm_api` | llm-api | Used in `DB_POSTGRESQL_WRITE_DSN` | -| `POSTGRES_SSL_MODE` | string | `disable` | llm-api | Used in `DB_POSTGRESQL_WRITE_DSN` | -| `POSTGRES_MAX_CONNECTIONS` | int | `100` | llm-api | New standardized var | -| `POSTGRES_MAX_IDLE_CONNS` | int | `5` | llm-api | New standardized var | -| `POSTGRES_MAX_OPEN_CONNS` | int | `15` | llm-api | New standardized var | -| `DB_CONN_MAX_LIFETIME` | duration | `30m` | llm-api | OK Already aligned | - -**Migration Notes:** -- All services use `DB_POSTGRESQL_WRITE_DSN` for database connections -- Connection URL is built from components: `postgres://user:password@host:port/database?sslmode=disable` -- This allows better secret management (password separate from URL) - -### Authentication (Keycloak) - -| Centralized Env Var | Type | Default | Services Using | Notes | -|---------------------|------|---------|----------------|-------| -| `KEYCLOAK_BASE_URL` | string | `http://keycloak:8085` | llm-api | OK Already aligned | -| `KEYCLOAK_REALM` | string | `jan` | llm-api | OK Already aligned | -| `KEYCLOAK_HTTP_PORT` | int | `8085` | Infrastructure | New standardized var | -| `KEYCLOAK_ADMIN` | string | `admin` | llm-api | OK Already aligned | -| `KEYCLOAK_ADMIN_PASSWORD` | string | (secret) | llm-api | OK Already aligned | -| `KEYCLOAK_ADMIN_REALM` | string | `master` | llm-api | OK Already aligned | -| `KEYCLOAK_ADMIN_CLIENT_ID` | string | `admin-cli` | llm-api | OK Already aligned | -| `BACKEND_CLIENT_ID` | string | `backend` | llm-api | OK Already aligned | -| `BACKEND_CLIENT_SECRET` | string | (secret) | llm-api | OK Already aligned | -| `CLIENT` | string | `jan-client` | llm-api | OK Already aligned | -| `OAUTH_REDIRECT_URI` | string | `http://localhost:8000/auth/callback` | llm-api | OK Already aligned | -| `JWKS_URL` | string | (computed) | llm-api | OK Already aligned | -| `OIDC_DISCOVERY_URL` | string | (computed) | llm-api | New standardized var | -| `ISSUER` | string | `http://localhost:8085/realms/jan` | llm-api | OK Already aligned | -| `ACCOUNT` | string | `account` | llm-api | OK Already aligned | -| `JWKS_REFRESH_INTERVAL` | duration | `5m` | llm-api | OK Already aligned | -| `AUTH_CLOCK_SKEW` | duration | `60s` | llm-api | OK Already aligned | -| `GUEST_ROLE` | string | `guest` | llm-api | OK Already aligned | -| `KEYCLOAK_FEATURES` | []string | `token-exchange,preview` | Infrastructure | New standardized var | - -### Gateway (Kong) - -| Centralized Env Var | Type | Default | Services Using | Notes | -|---------------------|------|---------|----------------|-------| -| `KONG_HTTP_PORT` | int | `8000` | Infrastructure | New standardized var | -| `KONG_ADMIN_PORT` | int | `8001` | Infrastructure | New standardized var | -| `KONG_ADMIN_URL` | string | `http://kong:8001` | llm-api | OK Already aligned | -| `KONG_LOG_LEVEL` | string | `info` | Infrastructure | New standardized var | - -## Services - -### LLM API - -| Centralized Env Var | Type | Default | Current Var | Status | -|---------------------|------|---------|-------------|--------| -| `HTTP_PORT` | int | `8080` | `HTTP_PORT` | OK Aligned | -| `METRICS_PORT` | int | `9091` | `METRICS_PORT` | OK Aligned | -| `LOG_LEVEL` | string | `info` | `LOG_LEVEL` | OK Aligned | -| `LOG_FORMAT` | string | `json` | `LOG_FORMAT` | OK Aligned | -| `AUTO_MIGRATE` | bool | `true` | `AUTO_MIGRATE` | OK Aligned | -| `API_KEY_PREFIX` | string | `sk_live` | `API_KEY_PREFIX` | OK Aligned | -| `API_KEY_DEFAULT_TTL` | duration | `2160h` | `API_KEY_DEFAULT_TTL` | OK Aligned | -| `API_KEY_MAX_TTL` | duration | `2160h` | `API_KEY_MAX_TTL` | OK Aligned | -| `API_KEY_MAX_PER_USER` | int | `5` | `API_KEY_MAX_PER_USER` | OK Aligned | -| `MODEL_PROVIDER_SECRET` | string | `jan-model-provider-secret-2024` | `MODEL_PROVIDER_SECRET` | OK Aligned | -| `MODEL_SYNC_ENABLED` | bool | `true` | `MODEL_SYNC_ENABLED` | OK Aligned | -| `MODEL_SYNC_INTERVAL_MINUTES` | int | `60` | `MODEL_SYNC_INTERVAL_MINUTES` | OK Aligned | -| `MEDIA_RESOLVE_URL` | string | `http://kong:8000/media/v1/media/resolve` | `MEDIA_RESOLVE_URL` | OK Aligned | -| `MEDIA_RESOLVE_TIMEOUT` | duration | `5s` | `MEDIA_RESOLVE_TIMEOUT` | OK Aligned | -| `DOCUMENT_OCR_ENABLED` | bool | `false` | `DOCUMENT_OCR_ENABLED` | New | -| `DOCUMENT_OCR_TIMEOUT` | duration | `120s` | `DOCUMENT_OCR_TIMEOUT` | New | -| `DOCUMENT_OCR_MODEL` | string | `docling-v1` | `DOCUMENT_OCR_MODEL` | New | -| `DOCUMENT_MAX_BYTES` | int | `52428800` | `DOCUMENT_MAX_BYTES` | New | -| `DOCUMENT_SUPPORTED_TYPES` | string | (list) | `DOCUMENT_SUPPORTED_TYPES` | New | -| `DOCLING_ENABLED` | bool | `false` | `DOCLING_ENABLED` | New | -| `DOCLING_PROVIDER_URL` | string | (empty) | `DOCLING_PROVIDER_URL` | New | -| `DOCLING_API_KEY` | string | (secret) | `DOCLING_API_KEY` | New | - -**Provider Config:** -| Centralized Env Var | Type | Default | Current Var | Status | -|---------------------|------|---------|-------------|--------| -| `JAN_PROVIDER_CONFIGS_FILE` | string | `configs/providers.yml` | `JAN_PROVIDER_CONFIGS_FILE` | TODO Path may differ | -| `JAN_PROVIDER_CONFIG_SET` | string | `default` | `JAN_PROVIDER_CONFIG_SET` | OK Aligned | -| `JAN_PROVIDER_CONFIGS` | bool | `true` | `JAN_PROVIDER_CONFIGS` | OK Aligned | - -### MCP Tools - -| Centralized Env Var | Type | Default | Current Var | Status | -|---------------------|------|---------|-------------|--------| -| `MCP_TOOLS_HTTP_PORT` | int | `8091` | `HTTP_PORT` | TODO Need prefix | -| `MCP_TOOLS_LOG_LEVEL` | string | `info` | `LOG_LEVEL` | TODO Need prefix | -| `MCP_TOOLS_LOG_FORMAT` | string | `json` | `LOG_FORMAT` | TODO Need prefix | -| `MCP_SEARCH_ENGINE` | string | `serper` | `SEARCH_ENGINE` | TODO Need prefix | -| `SEARXNG_URL` | string | `http://searxng:8080` | `SEARXNG_URL` | OK Aligned | -| `VECTOR_STORE_URL` | string | `http://vector-store:3015` | `VECTOR_STORE_URL` | OK Aligned | -| `SANDBOXFUSION_URL` | string | `http://sandboxfusion:8080` | `SANDBOXFUSION_URL` | OK Aligned | -| `MCP_SANDBOX_REQUIRE_APPROVAL` | bool | `true` | `SANDBOX_REQUIRE_APPROVAL` | TODO Need prefix | -| `MCP_CONFIG_FILE` | string | `configs/mcp-providers.yml` | `MCP_CONFIG_FILE` | OK Aligned | - -**Migration Notes:** -- Add `MCP_` or `MCP_TOOLS_` prefix to disambiguate from other services -- HTTP_PORT collision with llm-api when running in same environment - -### Memory Tools - -| Centralized Env Var | Type | Default | Current Var | Status | -|---------------------|------|---------|-------------|--------| -| `MEMORY_TOOLS_PORT` | int | `8090` | `MEMORY_TOOLS_PORT` | OK Aligned | -| `DB_POSTGRESQL_WRITE_DSN` | string | (computed) | - | ✅ Standard | -| `DB_POSTGRESQL_READ1_DSN` | string | - | - | ✅ New (optional) | -| `MEMORY_LOG_LEVEL` | string | `info` | `LOG_LEVEL` | TODO Need prefix | -| `MEMORY_LOG_FORMAT` | string | `json` | `LOG_FORMAT` | TODO Need prefix | -| `EMBEDDING_SERVICE_URL` | string | - | `EMBEDDING_SERVICE_URL` | OK Aligned | -| `EMBEDDING_CACHE_TYPE` | string | `memory` | `EMBEDDING_CACHE_TYPE` | OK Aligned | -| `EMBEDDING_CACHE_REDIS_URL` | string | `redis://redis:6379/3` | `EMBEDDING_CACHE_REDIS_URL` | OK Aligned | -| `EMBEDDING_CACHE_KEY_PREFIX` | string | `emb:` | `EMBEDDING_CACHE_KEY_PREFIX` | OK Aligned | -| `EMBEDDING_CACHE_MAX_SIZE` | int | `10000` | `EMBEDDING_CACHE_MAX_SIZE` | OK Aligned | -| `EMBEDDING_CACHE_TTL` | duration | `1h` | `EMBEDDING_CACHE_TTL` | OK Aligned | - -**Migration Notes:** -- Database configuration uses `DB_POSTGRESQL_WRITE_DSN` and optional `DB_POSTGRESQL_READ1_DSN` for read replicas -- Supports read/write splitting for better scalability -- Can share database with other services or use separate database -- Read replica is optional; falls back to write DSN if not configured - -### Realtime API - -| Centralized Env Var | Type | Default | Current Var | Status | -|---------------------|------|---------|-------------|--------| -| `REALTIME_API_PORT` | int | `8186` | `REALTIME_API_PORT` | OK Aligned | -| `REALTIME_LOG_LEVEL` | string | `info` | `LOG_LEVEL` | TODO Need prefix | -| `LIVEKIT_WS_URL` | string | `wss://your-livekit-server.com` | `LIVEKIT_WS_URL` | OK Aligned | -| `LIVEKIT_API_KEY` | string | (secret) | `LIVEKIT_API_KEY` | OK Aligned | -| `LIVEKIT_API_SECRET` | string | (secret) | `LIVEKIT_API_SECRET` | OK Aligned | -| `LIVEKIT_TOKEN_TTL` | duration | `24h` | `LIVEKIT_TOKEN_TTL` | OK Aligned | -| `SESSION_STALE_TTL` | duration | `10m` | `SESSION_STALE_TTL` | OK Aligned | -| `SESSION_CLEANUP_INTERVAL` | duration | `15s` | `SESSION_CLEANUP_INTERVAL` | OK Aligned | -| `REALTIME_AUTH_ENABLED` | bool | `true` | `AUTH_ENABLED` | TODO Need prefix | - -**Migration Notes:** -- LiveKit configuration already uses proper prefixes -- Session management variables are clear and aligned -- Only generic LOG_LEVEL and AUTH_ENABLED need REALTIME_ prefix for consistency - -### Media API - -| Centralized Env Var | Type | Default | Current Var | Status | -|---------------------|------|---------|-------------|--------| -| `MEDIA_API_PORT` | int | `8285` | `HTTP_PORT` | TODO Need rename | -| `MEDIA_API_LOG_LEVEL` | string | `info` | `LOG_LEVEL` | TODO Need prefix | -| `MEDIA_MAX_UPLOAD_BYTES` | int | `20971520` | `MAX_UPLOAD_SIZE` | TODO Rename needed | -| `MEDIA_RETENTION_DAYS` | int | `30` | `RETENTION_DAYS` | TODO Need prefix | -| `MEDIA_PROXY_DOWNLOAD` | bool | `true` | `PROXY_DOWNLOAD` | TODO Need prefix | -| `MEDIA_REMOTE_FETCH_TIMEOUT` | duration | `15s` | `FETCH_TIMEOUT` | TODO Rename needed | -| `MEDIA_S3_ENDPOINT` | string | `https://s3.menlo.ai` | `S3_ENDPOINT` | TODO Need prefix | -| `MEDIA_S3_REGION` | string | `us-west-2` | `S3_REGION` | TODO Need prefix | -| `MEDIA_S3_BUCKET` | string | `platform-dev` | `S3_BUCKET` | TODO Need prefix | -| `MEDIA_S3_USE_PATH_STYLE` | bool | `true` | `S3_PATH_STYLE` | TODO Rename needed | -| `MEDIA_S3_PRESIGN_TTL` | duration | `168h` | `PRESIGN_TTL` | TODO Need prefix | -| `MEDIA_S3_ACCESS_KEY_ID` | string | (secret) | `AWS_ACCESS_KEY_ID` | TODO Rename for clarity | -| `MEDIA_S3_SECRET_ACCESS_KEY` | string | (secret) | `AWS_SECRET_ACCESS_KEY` | TODO Rename for clarity | - -**Migration Notes:** -- Most env vars need `MEDIA_` prefix to avoid conflicts -- S3 vars should use `MEDIA_S3_` prefix for clarity -- Consider AWS credential standardization - -### Response API - -| Centralized Env Var | Type | Default | Current Var | Status | -|---------------------|------|---------|-------------|--------| -| `RESPONSE_API_PORT` | int | `8082` | `HTTP_PORT` | TODO Need rename | -| `RESPONSE_API_LOG_LEVEL` | string | `info` | `LOG_LEVEL` | TODO Need prefix | -| `RESPONSE_LLM_API_URL` | string | `http://llm-api:8080` | `LLM_API_URL` | TODO Need prefix | -| `RESPONSE_MCP_TOOLS_URL` | string | `http://mcp-tools:8091` | `MCP_TOOLS_URL` | TODO Need prefix | -| `RESPONSE_MAX_TOOL_DEPTH` | int | `8` | `MAX_TOOL_DEPTH` | TODO Need prefix | -| `RESPONSE_TOOL_TIMEOUT` | duration | `45s` | `TOOL_TIMEOUT` | TODO Need prefix | - -## Monitoring - -### OpenTelemetry - -| Centralized Env Var | Type | Default | Services Using | Status | -|---------------------|------|---------|----------------|--------| -| `OTEL_ENABLED` | bool | `false` | All services | OK Standard | -| `OTEL_SERVICE_NAME` | string | `llm-api` | All services | TODO Service-specific | -| `OTEL_EXPORTER_OTLP_ENDPOINT` | string | `http://otel-collector:4318` | All services | OK Standard | -| `OTEL_HTTP_PORT` | int | `4318` | Infrastructure | New | -| `OTEL_GRPC_PORT` | int | `4317` | Infrastructure | New | - -### Prometheus - -| Centralized Env Var | Type | Default | Services Using | Status | -|---------------------|------|---------|----------------|--------| -| `PROMETHEUS_PORT` | int | `9090` | Infrastructure | New | - -### Grafana - -| Centralized Env Var | Type | Default | Services Using | Status | -|---------------------|------|---------|----------------|--------| -| `GRAFANA_PORT` | int | `3331` | Infrastructure | New | -| `GRAFANA_ADMIN_USER` | string | `admin` | Infrastructure | New | -| `GRAFANA_ADMIN_PASSWORD` | string | (secret) | Infrastructure | New | - -### Jaeger - -| Centralized Env Var | Type | Default | Services Using | Status | -|---------------------|------|---------|----------------|--------| -| `JAEGER_UI_PORT` | int | `16686` | Infrastructure | New | - -## Inference - -### vLLM - -| Centralized Env Var | Type | Default | Services Using | Status | -|---------------------|------|---------|-------------|--------| -| `VLLM_ENABLED` | bool | `true` | Infrastructure | New | -| `VLLM_PORT` | int | `8001` | llm-api | New | -| `VLLM_MODEL` | string | `Qwen/Qwen2.5-0.5B-Instruct` | Infrastructure | New | -| `VLLM_SERVED_NAME` | string | `qwen2.5-0.5b-instruct` | Infrastructure | New | -| `VLLM_GPU_UTILIZATION` | float | `0.66` | Infrastructure | New | - -## Migration Priority - -### Phase 1: Critical (Sprint 3.1) -OK **Already Aligned - No Changes Needed:** -- llm-api authentication vars (Keycloak) -- llm-api API key management -- llm-api model sync -- Database connection timeouts - -### Phase 2: High Priority (Sprint 3.2) -TODO **Requires Prefix/Rename:** -- Service-specific HTTP_PORT -> \{SERVICE\}_PORT -- Service-specific LOG_LEVEL -> \{SERVICE\}_LOG_LEVEL -- Database URL components - -### Phase 3: Medium Priority (Sprint 3.3) -TODO **New Variables - Add Support:** -- Infrastructure monitoring ports (Prometheus, Grafana, Jaeger) -- vLLM inference configuration -- Kong gateway ports -- Database connection pool settings - -### Phase 4: Low Priority (Sprint 3.4) -TODO **Nice to Have:** -- Media API S3 prefixing -- Response API prefixing -- MCP Tools prefixing - -## Testing Strategy - -### Per-Service Testing - -For each service after env var migration: - -1. **Unit Tests:** Verify config loading with new env vars -2. **Integration Tests:** Test with Docker Compose -3. **Precedence Tests:** Verify env vars override defaults -4. **Backward Compatibility:** Old env vars still work (deprecation warnings) - -### Test Script Template - -```bash -#!/bin/bash -# Test service with new env vars - -# Set centralized env vars -export POSTGRES_HOST=testdb -export POSTGRES_PORT=5432 -export POSTGRES_USER=testuser -export POSTGRES_PASSWORD=testpass -export POSTGRES_DB=testdb -export POSTGRES_SSL_MODE=disable - -# Run service -./service-binary - -# Verify config loaded correctly -curl http://localhost:8080/health -``` - -## Rollback Plan - -If migration causes issues: - -1. **Immediate:** Revert docker compose.yml to use old env vars -2. **Service-Level:** Keep backward compatibility (read both old and new vars) -3. **Gradual Migration:** Migrate one service at a time, not all at once - -## See Also - -- [Service Migration Strategy](./service-migration-strategy) -- [Configuration Precedence](./precedence) -- [Configuration Types Reference](../../pkg/config/types.go) - diff --git a/apps/platform/content/docs/configuration/index.mdx b/apps/platform/content/docs/configuration/index.mdx deleted file mode 100644 index 443d5e7f..00000000 --- a/apps/platform/content/docs/configuration/index.mdx +++ /dev/null @@ -1,398 +0,0 @@ ---- -title: "Configuration System" ---- - -# Configuration System - -Jan Server uses a simple configuration system with default values that you can override. - -## Why This Matters - -- **Safe:** Catches configuration errors before startup -- **Clear:** Easy to see what settings are available -- **Flexible:** Works in development and production -- **Validated:** Tells you exactly what's wrong if config is invalid - -## How It Works - -Configuration is loaded in this order (later overrides earlier): - -1. **YAML defaults** - Built-in sensible defaults (`config/defaults.yaml`) -2. **Environment file** - Your specific settings (`config/production.yaml`) -3. **Environment variables** - Highest priority (great for secrets) - -## Quick Commands - -```bash -# Check if your configuration is valid -jan-cli config validate - -# See all current settings -jan-cli config export - -# See settings for one service -jan-cli config show llm-api -``` - -For the full set of commands (for example `config generate`, `config k8s-values`, and `config drift`), see the [Jan CLI Guide](../guides/jan-cli). - -## Documentation Structure - -This directory contains configuration system implementation details: - -| Document | Description | -|----------|-------------| -| [precedence.md](./precedence) | Configuration precedence rules and loading order | -| [env-var-mapping.md](./env-var-mapping) | Environment variable to config mapping | -| [docker-compose.md](./docker-compose) | Docker Compose integration | -| [kubernetes.md](./kubernetes) | Kubernetes Helm values generation | -| [service-migration.md](./service-migration) | Migrating services to new config system | - -**For user-facing documentation:** -- **[Jan CLI Guide](../guides/jan-cli)** - Command-line tool for configuration management -- **[Testing Guide](../guides/testing)** - Cross-platform testing procedures - -## Configuration Files - -### defaults.yaml - -Base configuration with sensible defaults for all environments: - -```yaml -environment: development - -services: - llm-api: - http: - port: 8080 - timeout: 30s - database: - dsn: postgres://jan_user:jan_password@localhost:5432/jan_llm_api - max_idle_conns: 10 - max_open_conns: 30 - auth: - enabled: true - issuer: http://localhost:8085/realms/jan -``` - -### Environment-specific YAMLs - -Optional overrides live in `config/environments/.yaml`. The directory is not created by default---add it as needed: - -```yaml -# config/environments/production.yaml -environment: production - -services: - llm-api: - http: - timeout: 60s - database: - max_idle_conns: 20 - max_open_conns: 100 - observability: - enabled: true - endpoint: https://otel-collector.prod.example.com -``` - -When you run the loader with `environment=production`, the stack becomes: -1. Struct defaults (priority 100) -2. `config/defaults.yaml` (priority 200) -3. `config/environments/production.yaml` (priority 300) --- **create this file** -4. Environment variables (priority 500) - -### Environment Files (`.env`) - -The repo ships with ready-made `.env` templates for Docker workflows: - -| File | Purpose | -|------|---------| -| `.env.template` | Base template used by `make quickstart` | -| `.env` | Generated interactive configuration (git-ignored) | -| `config/production.env.example` | Example values for production CI/CD | -| `config/secrets.env.example` | Placeholder for sensitive values (copy to `config/secrets.env`) | - -Use `make quickstart` or `make setup` to populate `.env`; copy the example files when preparing staging/production pipelines. - -### Environment Variables - -Highest priority - override any YAML setting: - -```bash -# Override HTTP port -export LLM_API_HTTP_PORT=9090 - -# Override database connection -export LLM_API_DATABASE_DSN=postgres://user:pass@prod-db:5432/db - -# Override observability -export LLM_API_OBSERVABILITY_ENABLED=true -``` - -## Common Tasks - -### Adding a New Configuration Field - -1. **Update Go struct** in `pkg/config/types.go`: - ```go - type HTTPConfig struct { - Port int `yaml:"port" env:"HTTP_PORT" default:"8080"` - Timeout time.Duration `yaml:"timeout" env:"HTTP_TIMEOUT" default:"30s"` - // Add new field - MaxBodySize int64 `yaml:"max_body_size" env:"HTTP_MAX_BODY_SIZE" default:"10485760"` - } - ``` - -2. **Regenerate config files**: - ```bash - make config-generate - ``` - -3. **Update defaults.yaml** (if auto-generated values aren't sufficient): - ```yaml - services: - llm-api: - http: - max_body_size: 10485760 # 10 MB - ``` - -4. **Test your changes**: - ```bash - make config-test - jan-cli config validate - ``` - -### Validating Configuration - -```bash -# Validate current configuration -jan-cli config validate - -# Validate with specific environment -ENVIRONMENT=production jan-cli config validate - -# Check for configuration drift (CI/CD) -make config-drift-check -``` - -### Generating Kubernetes Values - -```bash -# Generate values for all environments -jan-cli config k8s-values --env development > k8s/jan-server/values-development.yaml -jan-cli config k8s-values --env production > k8s/jan-server/values-production.yaml - -# Generate with overrides -jan-cli config k8s-values --env production \ - --set services.llm-api.replicas=3 \ - --set services.llm-api.resources.limits.memory=2Gi \ - > k8s/values-prod-scaled.yaml -``` - -## Architecture - -### Package Structure - -``` -pkg/config/ -+-- types.go # Configuration structs (source of truth) -+-- loader.go # YAML + env loading logic -+-- validation.go # Validation rules -+-- provenance.go # Track config source -+-- env.go # Environment variable helpers -+-- k8s/ -| +-- values_generator.go # Helm values generator -+-- testdata/ # Test fixtures - -config/ -+-- defaults.yaml # Auto-generated base defaults -+-- development.yaml # Dev overrides (optional) -+-- staging.yaml # Staging overrides (optional) -+-- production.yaml # Production overrides (optional) - -tools/jan-cli/ -+-- main.go # CLI tool -``` - -### Design Principles - -1. **Single Source of Truth:** Go structs define all configuration -2. **Auto-Generation:** YAML, JSON Schema, and docs generated from code -3. **Fail Fast:** Validation at startup prevents runtime errors -4. **Environment Parity:** Same config structure across all environments -5. **Override by Exception:** Defaults work everywhere, override only what's different -6. **Explicit Over Implicit:** No magic values or hidden defaults - -## Migration Guide - -Migrating from old environment-variable-only approach: - -### Before (Old Way) - -```go -type Config struct { - HTTPPort int `env:"HTTP_PORT" envDefault:"8080"` - DBPostgresqlWriteDSN string `env:"DB_POSTGRESQL_WRITE_DSN,notEmpty"` - LogLevel string `env:"LOG_LEVEL" envDefault:"info"` - AuthEnabled bool `env:"AUTH_ENABLED" envDefault:"false"` - //... 50+ more variables -} -``` - -**Problems:** -- 50+ environment variables per service -- No validation until runtime -- Hard to see effective configuration -- Difficult to manage across environments -- No documentation of what's actually used - -### After (New Way) - -```go -import "jan-server/pkg/config" - -cfg, _:= config.Load() -serviceCfg, _:= cfg.GetServiceConfig("llm-api") - -// Type-safe access -port:= serviceCfg.HTTP.Port -dbDSN:= serviceCfg.Database.DSN -``` - -**Benefits:** -- ~10 environment variables per service (only overrides) -- Validated at load time -- `jan-cli config export` shows effective config -- YAML files for environment differences -- Auto-generated documentation - -See [service-migration.md](./service-migration) for detailed migration steps. - -## Best Practices - -### 1. Use Defaults for Common Values - -Put shared defaults in `config/defaults.yaml`: - -```yaml -# Good: Shared defaults -services: - llm-api: - http: - timeout: 30s - port: 8080 -``` - -### 2. Override Only What's Different - -Environment-specific files should be minimal: - -```yaml -# config/production.yaml - Only overrides -services: - llm-api: - database: - max_open_conns: 100 # Higher for production - observability: - enabled: true -``` - -### 3. Use Environment Variables for Secrets - -Never put secrets in YAML files: - -```bash -#.env or CI/CD secrets -export LLM_API_DATABASE_DSN=postgres://user:${DB_PASSWORD}@prod-db/db -export LLM_API_AUTH_CLIENT_SECRET=${KEYCLOAK_CLIENT_SECRET} -``` - -### 4. Validate Early - -Add validation to your config loading: - -```go -cfg, err:= config.Load() -if err != nil { - log.Fatal("invalid configuration: %w", err) -} -``` - -### 5. Use CLI Tools in CI/CD - -Prevent configuration drift: - -```yaml -#.github/workflows/ci.yml -- name: Validate configuration - run: | - make config-drift-check - jan-cli config validate -``` - -## Troubleshooting - -### Configuration Not Loading - -```bash -# Check what's being loaded -jan-cli config show llm-api - -# Validate configuration -jan-cli config validate - -# Export effective config -jan-cli config export -``` - -### Environment Variable Not Working - -Check the naming convention - use service prefix: - -```bash -# Wrong -export HTTP_PORT=9090 - -# Correct -export LLM_API_HTTP_PORT=9090 -``` - -### Kubernetes Values Not Applying - -Regenerate values after config changes: - -```bash -make config-generate -jan-cli config k8s-values --env production > k8s/values-prod.yaml -helm upgrade jan-server k8s/jan-server -f k8s/values-prod.yaml -``` - -## Reference - -### Documentation -- **CLI Guide:** [docs/guides/jan-cli.md](../guides/jan-cli) - Installation, usage, and examples -- **CLI Command Reference:** [tools/jan-cli/README.md](../../tools/jan-cli/) -- **Configuration Types:** [pkg/config/README.md](../../pkg/config/) - -### Code References -- **Go Package:** `pkg/config/` in workspace root -- **Default Config:** [config/defaults.yaml](../../config/defaults.yaml) -- **JSON Schema:** [config-schema.json](config-schema.json) (auto-generated) - -## Examples - -See working examples in: -- **LLM API:** `services/llm-api/internal/config/` -- **Template API:** `services/template-api/internal/config/` (shows both approaches) -- **MCP Tools:** `services/mcp-tools/configs/` - ---- - -**Need help?** See [service-migration.md](./service-migration) or check existing service implementations for patterns. - - - - - - - diff --git a/apps/platform/content/docs/configuration/kubernetes.mdx b/apps/platform/content/docs/configuration/kubernetes.mdx deleted file mode 100644 index 45d9aa84..00000000 --- a/apps/platform/content/docs/configuration/kubernetes.mdx +++ /dev/null @@ -1,333 +0,0 @@ ---- -title: "Kubernetes Helm Values Generation" ---- - -# Kubernetes Helm Values Generation - -Automatic generation of Kubernetes Helm `values.yaml` files from Jan Server configuration. - -## Overview - -The K8s values generator maps Jan Server's unified configuration to Helm chart values, enabling: - -- **Single source of truth**: Configuration drives both local and K8s deployments -- **Environment-specific overrides**: Development, staging, production profiles -- **Consistent deployment**: Same config structure across all environments -- **Type safety**: Generated from Go structs with validation - -## Architecture - -``` -config/defaults.yaml - v - Config Loader - v -Values Generator -----> values-dev.yaml (1 replica, minimal resources) - +------------> values-prod.yaml (3 replicas, full resources, persistence) -(Add values-staging.yaml as needed by copying values-dev.yaml) -``` - -## Generated Structure - -### Global Values -```yaml -global: - environment: development - imagePullPolicy: IfNotPresent - labels: - app.kubernetes.io/name: jan-server - app.kubernetes.io/version: 1.0.0 - app.kubernetes.io/environment: development -``` - -### Service Values -Each service gets: -- **Deployment**: replicas, image, resources -- **Service**: type, ports -- **Health Checks**: liveness/readiness probes -- **Config**: ConfigMaps for non-sensitive config -- **Secrets**: References to K8s secrets - -Example: -```yaml -services: - llm-api: - enabled: true - replicaCount: 2 - image: - repository: jan-llm-api - tag: 1.0.0 - service: - type: ClusterIP - port: 8080 - targetPort: 8080 - resources: - limits: - cpu: 1000m - memory: 1Gi - requests: - cpu: 500m - memory: 512Mi - healthChecks: - livenessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 30 - readinessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 10 - configMap: - LOG_LEVEL: info - LOG_FORMAT: json - secrets: - - database-credentials - - keycloak-credentials -``` - -### Infrastructure Values -Database and auth configuration: -```yaml -infrastructure: - database: - postgres: - enabled: true - host: api-db - port: 5432 - database: jan_llm_api - user: jan_user - passwordSecret: postgres-password - maxConnections: 100 - resources: - limits: - cpu: 2000m - memory: 2Gi - persistence: - enabled: true - size: 10Gi - auth: - keycloak: - enabled: true - baseURL: http://keycloak:8085 - adminUser: admin - passwordSecret: keycloak-admin-password - resources: - limits: - cpu: 1000m - memory: 1Gi -``` - -## Environment Profiles - -### Development -- Single replica per service -- Minimal resources (100m CPU, 128Mi RAM requests) -- `imagePullPolicy: Never` (use local images) -- Persistence disabled -- Lower health check thresholds - -### Staging -- 2 replicas per service -- Moderate resources (250m CPU, 256Mi RAM requests) -- `imagePullPolicy: IfNotPresent` -- Persistence enabled (20Gi) -- Production-like settings - -### Production -- 3 replicas per service -- Full resources (500m CPU, 512Mi RAM requests) -- `imagePullPolicy: Always` -- Persistence enabled (50Gi) -- Strict health checks -- Higher failure thresholds - -## Usage - -### Programmatic Generation - -```go -package main - -import ( - "context" - "github.com/janhq/jan-server/pkg/config" - "github.com/janhq/jan-server/pkg/config/k8s" -) - -func main() { - // Load configuration - loader:= config.NewConfigLoader("development", "config/defaults.yaml") - cfg, _:= loader.Load(context.Background()) - - // Create generator - generator:= k8s.NewValuesGenerator(cfg) - - // Generate base values - generator.GenerateToFile("values.yaml") - - // Generate with environment overrides - values, _:= generator.GenerateWithOverrides("production") - // Use values... -} -``` - -### Example Program - -See `pkg/config/k8s/examples/generate_values.go`: - -```bash -cd pkg/config/k8s/examples -go run generate_values.go -``` - -Generates: -- `values-dev.yaml` - Development environment -- `values-prod.yaml` - Production environment - -## Configuration Mapping - -| Config Path | Helm Values Path | Notes | -|-------------|-----------------|-------| -| `meta.version` | `global.labels["app.kubernetes.io/version"]`, `*.image.tag` | Version across all components | -| `meta.environment` | `global.environment`, `global.labels["app.kubernetes.io/environment"]` | Environment name | -| `services.llm_api.http_port` | `services.llm-api.service.port` | Service port mapping | -| `services.llm_api.log_level` | `services.llm-api.configMap.LOG_LEVEL` | Config as ConfigMap | -| `infrastructure.database.postgres.*` | `infrastructure.database.postgres.*` | Direct mapping | -| `infrastructure.auth.keycloak.*` | `infrastructure.auth.keycloak.*` | Direct mapping | - -## Secret Management - -**Important**: The generator creates **references** to secrets, not the secrets themselves. - -Secret references in values: -```yaml -services: - llm-api: - secrets: - - database-credentials # -> maps to K8s Secret - - keycloak-credentials -``` - -You must create K8s secrets separately: -```bash -kubectl create secret generic database-credentials \ - --from-literal=password= - -kubectl create secret generic keycloak-credentials \ - --from-literal=admin-password= -``` - -Secrets are managed by DevOps via Kubernetes Secrets, HashiCorp Vault, or environment variables. - -## Resource Sizing - -### Default Resources - -| Service | CPU Request | CPU Limit | Memory Request | Memory Limit | -|---------|------------|-----------|----------------|--------------| -| llm-api | 500m | 1000m | 512Mi | 1Gi | -| mcp-tools | 250m | 500m | 256Mi | 512Mi | -| media-api | 250m | 500m | 256Mi | 512Mi | -| response-api | 250m | 500m | 256Mi | 512Mi | -| postgres | 1000m | 2000m | 1Gi | 2Gi | -| keycloak | 500m | 1000m | 512Mi | 1Gi | - -### Scaling Recommendations - -For production workloads: -- **High traffic**: Increase replicas (3-5+) -- **Heavy AI workloads**: Increase llm-api resources (2-4 CPU, 2-4Gi RAM) -- **Large databases**: Increase postgres resources and persistence size - -## Health Checks - -### Liveness Probes -- Detect crashed containers -- Restart unhealthy containers -- Higher failure threshold (3-5) - -### Readiness Probes -- Detect startup completion -- Remove from service during issues -- Lower failure threshold (3) - -Generated probes: -```yaml -livenessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 3 -readinessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 10 - periodSeconds: 5 - timeoutSeconds: 3 - failureThreshold: 3 -``` - -## Integration with Helm - -### Deploy with Generated Values - -```bash -# Generate values -go run pkg/config/k8s/examples/generate_values.go - -# Install chart with generated values -helm install jan-server k8s/jan-server \ - -f pkg/config/k8s/examples/values-prod.yaml -``` - -### Override Specific Values - -```bash -# Use generated base + custom overrides -helm install jan-server k8s/jan-server \ - -f values-prod.yaml \ - --set services.llm-api.replicaCount=5 -``` - -### CI/CD Integration - -```yaml -#.github/workflows/deploy.yml -- name: Generate Helm Values - run: | - go run pkg/config/k8s/examples/generate_values.go - -- name: Deploy to K8s - run: | - helm upgrade --install jan-server k8s/jan-server \ - -f values-prod.yaml \ - --namespace jan-server -``` - -## Limitations - -- **Not a replacement for Helm templating**: Generator creates values, Helm chart still needed -- **Basic resource sizing**: May need tuning based on actual workload -- **Secrets not generated**: K8s secrets must be created separately by DevOps team -- **No autoscaling**: HPA configuration not generated (add manually) - -## Future Enhancements - -- [ ] Autoscaling (HPA) configuration -- [ ] Ingress generation -- [ ] Network policies -- [ ] Pod disruption budgets -- [ ] Service mesh integration (Istio, Linkerd) -- [ ] Custom resource definitions (CRDs) - -## See Also - -- [Configuration System](./index) -- [Helm Chart Documentation](../../k8s/jan-server/) -- [Deployment Guide](../../docs/guides/deployment) diff --git a/apps/platform/content/docs/configuration/meta.json b/apps/platform/content/docs/configuration/meta.json deleted file mode 100644 index 216cb90c..00000000 --- a/apps/platform/content/docs/configuration/meta.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "title": "Configuration", - "pages": [ - "index", - "docker-compose", - "kubernetes", - "env-var-mapping", - "precedence", - "service-migration" - ] -} diff --git a/apps/platform/content/docs/configuration/precedence.mdx b/apps/platform/content/docs/configuration/precedence.mdx deleted file mode 100644 index 1c131aa3..00000000 --- a/apps/platform/content/docs/configuration/precedence.mdx +++ /dev/null @@ -1,502 +0,0 @@ ---- -title: "Configuration Precedence" ---- - -# Configuration Precedence - -How Jan Server loads configuration values and decides which source wins. - -## Quick Summary - -| Priority | Source | Notes | -|----------|--------|-------| -| **600** | CLI flags (future) | Planned for jan-cli integrations | -| **500** | Environment variables | Secrets, overrides, CI/CD | -| **300** | `config/environments/*.yaml` | Per-environment overrides | -| **200** | `config/defaults.yaml` | Generated defaults (`make config-generate`) | -| **100** | Struct tags (`envDefault`) | Absolute fallback values | - -**Rule**: the highest priority number wins every time. Example: `POSTGRES_PORT=5433` (priority 500) beats `port: 5432` in `defaults.yaml` (priority 200). - -## Configuration Sources - -### 1. StructDefaultSource (Priority 100) - -**Lowest priority**. These are the hardcoded defaults embedded in Go struct tags in `pkg/config/types.go`. - -```go -type PostgresConfig struct { - Port int `yaml:"port" json:"port" env:"POSTGRES_PORT" envDefault:"5432"` - // ^^^^^^^^^^^^^^^^ - // Priority 100 -} - -// NOTE: Never store secrets in envDefault tags -``` - - -**When to use:** Never set directly - these are fallback defaults. - -**Example:** -```go -// From types.go -Host string `envDefault:"api-db"` // Priority 100: "api-db" -``` - -### 2. YAMLDefaultSource (Priority 200) - -**Second priority**. Auto-generated `config/defaults.yaml` created by `make config-generate`. - -```yaml -infrastructure: - database: - postgres: - host: "api-db" # Priority 200 - port: 5432 # Priority 200 - user: "jan_user" -``` - -**When to use:** Baseline configuration for every environment. Generated via `make config-generate`; never edit manually. - -**Example:** -```bash -# Generate defaults.yaml -make config-generate - -# This creates config/defaults.yaml with all default values -``` - -### 3. YAMLEnvSource (Priority 300) - -**Third priority**. Environment-specific configuration files in `config/environments/*.yaml`. - -```yaml -# config/environments/production.yaml -infrastructure: - database: - postgres: - host: "prod-db.example.com" # Priority 300: overrides defaults.yaml - port: 5432 # Priority 300: same as default, redundant - max_connections: 500 # Priority 300: production tuning -``` - -**When to use:** -- Production/staging-specific settings -- Infrastructure endpoints that differ per environment -- Feature flags per environment - -**File naming:** -- `config/environments/development.yaml` -- `config/environments/staging.yaml` -- `config/environments/production.yaml` - -**Example:** -```bash -# Load development environment -loader:= config.NewConfigLoader("development", "config/defaults.yaml") -cfg, err:= loader.Load(context.Background()) - -# Loads in order: -# 1. Struct defaults (100) -# 2. config/defaults.yaml (200) -# 3. config/environments/development.yaml (300) <- Environment-specific -# 4. Environment variables (500) -``` - -### 4. EnvVarSource (Priority 500) - -**Fourth priority**. System environment variables with names matching `env` struct tags. - -```bash -# Priority 500: Overrides everything except CLI flags -export POSTGRES_HOST=override-db -export POSTGRES_PORT=5433 -export AUTO_MIGRATE=false -``` - -**When to use:** -- Docker/Kubernetes deployments -- CI/CD pipelines -- Local development overrides -- Secrets and sensitive values (managed by DevOps) - -**Tag mapping:** -```go -// From types.go -Host string `env:"POSTGRES_HOST"` // Set with: export POSTGRES_HOST=value -Port int `env:"POSTGRES_PORT"` // Set with: export POSTGRES_PORT=5433 -``` - -**Example:** -```bash -# Override database host for local testing -export POSTGRES_HOST=localhost -export POSTGRES_PORT=5433 - -# Run service - will use localhost:5433 instead of config values -./llm-api -``` - -### 5. CLI Flags (Priority 600) - Planned - -**Highest priority**. Command-line flags (not yet implemented, planned for Sprint 7+). - -```bash -# Planned for future -./llm-api --db-host=emergency-db --db-port=5434 -``` - -**When to use:** -- Emergency overrides -- One-time testing -- Troubleshooting in production - -## Conflict Resolution - -### Merge Strategy - -The configuration loader uses a **non-zero override** strategy: - -1. Start with an empty `Config` struct -2. Load sources in ascending priority order (100 -> 200 -> 300 -> 500) -3. For each source: - - Load configuration values - - **Only non-zero values** from the source override existing values - - Zero values are skipped (don't override with empty strings, 0, false) - -### Example: Port Precedence - -```go -// Initial state (empty Config) -Port: 0 - -// Load StructDefaultSource (Priority 100) -Port: 5432 // From envDefault tag - -// Load YAMLDefaultSource (Priority 200) -Port: 5432 // defaults.yaml matches struct default, no change - -// Load YAMLEnvSource (Priority 300) -Port: 5433 // production.yaml overrides to 5433 - -// Load EnvVarSource (Priority 500) -Port: 5434 // POSTGRES_PORT env var wins - // Final value: 5434 -``` - -### Provenance Tracking - -The loader tracks which source provided each final value: - -```go -loader:= config.NewConfigLoader("production", "config/defaults.yaml") -cfg, _:= loader.Load(ctx) - -// Print configuration sources -fmt.Println(loader.Provenance()) - -// Output: -// Configuration Sources (priority order): -// [100] struct-defaults -// [200] yaml-defaults -// [300] yaml-env-production -// [500] env-vars -// -// Loaded 127 configuration values -``` - -### Debug: Finding Value Origin - -To debug where a specific value came from: - -```go -info, err:= loader.Provenance("infrastructure.database.postgres.port") -if err == nil { - fmt.Printf("Port came from: %s (priority %d)\n", info.Source, info.Priority) - // Output: Port came from: env-vars (priority 500) -} -``` - -## Common Scenarios - -### Scenario 1: Development Override - -**Goal:** Use localhost database for local development - -```bash -# Set environment variables -export POSTGRES_HOST=localhost -export POSTGRES_PORT=5432 - -# Run service -go run./cmd/server -``` - -**Result:** -- `POSTGRES_HOST`: localhost (priority 500, env var) -- `POSTGRES_PORT`: 5432 (priority 500, env var) -- All other settings: from defaults.yaml (priority 200) - -### Scenario 2: Production Deployment - -**Goal:** Use production database with secrets - -```yaml -# config/environments/production.yaml (priority 300) -infrastructure: - database: - postgres: - host: "prod-db.company.com" - ssl_mode: "require" -``` - -```bash -# Kubernetes secret (priority 500) -export POSTGRES_PASSWORD= -export POSTGRES_USER= - -# Run with production environment -./llm-api --environment=production -``` - -**Result:** -- `host`: prod-db.company.com (priority 300, environment YAML) -- `ssl_mode`: require (priority 300, environment YAML) -- `user`, `password`: from env vars (priority 500, secrets) -- All other settings: from defaults.yaml (priority 200) - -### Scenario 3: Temporary Override - -**Goal:** Test with different port without changing config - -```bash -# One-time override for testing -export POSTGRES_PORT=9999 - -# Run test -go test./... - -# Unset when done -unset POSTGRES_PORT -``` - -**Result:** -- `POSTGRES_PORT`: 9999 (priority 500) during test -- All other services still use 5432 from defaults - -## Data Type Handling - -### Strings -```bash -export POSTGRES_HOST=localhost # Simple string -export KEYCLOAK_REALM=jan # No quotes needed -``` - -### Integers -```bash -export POSTGRES_PORT=5433 # Parsed as int -export POSTGRES_MAX_CONNECTIONS=200 -``` - -### Booleans -```bash -export AUTO_MIGRATE=true # Accepts: true, false, 1, 0 -export OTEL_ENABLED=false -``` - -### Durations -```bash -export DB_CONN_MAX_LIFETIME=45m # Go duration format -export MEDIA_S3_PRESIGN_TTL=10m # Supports: ns, us, ms, s, m, h -``` - -### Slices (comma-separated) -```bash -export KEYCLOAK_FEATURES=token-exchange,preview,admin-api -# Parsed as: []string{"token-exchange", "preview", "admin-api"} -``` - -## Best Practices - -### OK DO - -1. **Use environment variables for secrets** - ```bash - export POSTGRES_PASSWORD=$VAULT_SECRET - export AWS_ACCESS_KEY_ID=$AWS_KEY - ``` - -2. **Use environment YAML for per-environment infrastructure** - ```yaml - # config/environments/staging.yaml - infrastructure: - database: - postgres: - host: "staging-db.internal" - ``` - -3. **Keep defaults.yaml comprehensive** - ```bash - # Regenerate after adding new config fields - make config-generate - ``` - -4. **Document precedence in comments** - ```yaml - # config/environments/production.yaml - infrastructure: - database: - postgres: - # Override for production (priority 300) - # Can still be overridden by POSTGRES_HOST env var (priority 500) - host: "prod-db.company.com" - ``` - -### [X] DON'T - -1. **Don't manually edit defaults.yaml** - ```bash - # [X] WRONG: Manual edits will be overwritten - vim config/defaults.yaml - - # OK CORRECT: Edit types.go and regenerate - vim pkg/config/types.go - make config-generate - ``` - -2. **Don't put secrets in YAML files** - ```yaml - # [X] WRONG: Secret in version control - infrastructure: - database: - postgres: - password: "super-secret-123" # DON'T DO THIS - - # OK CORRECT: Use environment variable (managed by DevOps) - # export POSTGRES_PASSWORD=super-secret-123 - # Or use K8s Secrets, Vault, etc. - ``` - -3. **Don't override everything in environment YAML** - ```yaml - # [X] WRONG: Duplicating all defaults - infrastructure: - database: - postgres: - host: "prod-db.com" - port: 5432 # Redundant with default - user: "jan_user" # Redundant with default - database: "jan_llm_api" # Redundant with default - - # OK CORRECT: Only override what's different - infrastructure: - database: - postgres: - host: "prod-db.com" # Only this is different - ``` - -4. **Don't rely on zero-value overrides** - ```bash - # [X] WRONG: Trying to "unset" a value - export AUTO_MIGRATE= # Empty string won't override true - - # OK CORRECT: Explicitly set to false - export AUTO_MIGRATE=false - ``` - -## Testing Precedence - -### Unit Test Example - -```go -func TestConfigPrecedence(t *testing.T) { - // Set environment variable (priority 500) - os.Setenv("POSTGRES_PORT", "9999") - defer os.Clearenv() - - // Create YAML file (priority 200) - yaml:= ` -infrastructure: - database: - postgres: - port: 5433 -` - os.WriteFile("config/defaults.yaml", []byte(yaml), 0644) - - // Load configuration - loader:= config.NewConfigLoader("development", "config/defaults.yaml") - cfg, err:= loader.Load(context.Background()) - - // Environment variable should win - assert.Equal(t, 9999, cfg.Infrastructure.Database.Postgres.Port) - - // Check provenance - prov:= loader.Provenance() - assert.Contains(t, prov, "env-vars") // Confirm env vars were loaded -} -``` -``` - -## Troubleshooting - -### Problem: Configuration not being applied - -**Symptom:** Set `POSTGRES_PORT=9999` but service still uses 5432 - -**Solution:** -1. Check environment variable name matches `env` tag: - ```go - Port int `env:"POSTGRES_PORT"` // Must be exact match - ``` - -2. Verify environment variable is set in correct shell: - ```bash - printenv | grep POSTGRES_PORT - ``` - -3. Check provenance to see what overrode it: - ```go - info, _:= loader.Provenance("infrastructure.database.postgres.port") - fmt.Printf("Source: %s (priority %d)\n", info.Source, info.Priority) - ``` - -### Problem: Can't override YAML value - -**Symptom:** Changed `production.yaml` but using old value - -**Solution:** -1. Check file path matches environment: - ```bash - ls config/environments/production.yaml # Must exist - ``` - -2. Verify you're loading correct environment: - ```go - loader:= config.NewConfigLoader("production", "config/defaults.yaml") - // ^^^^^^^^^^^ Must match filename - ``` - -3. Check for environment variable override (priority 500 > 300): - ```bash - unset POSTGRES_HOST # Remove higher-priority override - ``` - -### Problem: Defaults not updating - -**Symptom:** Changed `envDefault` tag but `defaults.yaml` unchanged - -**Solution:** -```bash -# Regenerate defaults.yaml -make config-generate - -# Verify CI drift detection passes -go test -v./pkg/config -run TestConfigDrift -``` - -## See Also - -- [Configuration README](../../pkg/config/) - Implementation details -- [Config Types Reference](../../pkg/config/types.go) - All configuration fields -- [Code Generation](../../pkg/config/codegen/) - Schema and YAML generators -- [Loader Tests](../../pkg/config/loader_test.go) - Precedence test examples diff --git a/apps/platform/content/docs/configuration/service-migration.mdx b/apps/platform/content/docs/configuration/service-migration.mdx deleted file mode 100644 index 0751fdcc..00000000 --- a/apps/platform/content/docs/configuration/service-migration.mdx +++ /dev/null @@ -1,169 +0,0 @@ ---- -title: "Sprint 3: Service Migration Strategy" ---- - -# Sprint 3: Service Migration Strategy - -## Overview - -Sprint 3 involves migrating services (llm-api, mcp-tools, media-api, response-api) to use the centralized configuration system in `pkg/config`. - -## Challenge: Module Dependencies - -The jan-server project uses a **workspace** structure where each service is a separate Go module: -- `services/llm-api` has its own `go.mod` -- `services/mcp-tools` has its own `go.mod` -- `pkg/config` is at the root workspace level - -**Problem:** Services cannot directly import `github.com/janhq/jan-server/pkg/config` without either: -1. Restructuring into a monorepo with shared pkg/ -2. Publishing pkg/config as a separate module -3. Using Go workspace features to share the package - -## Recommended Approach - -### Phase 1: Environment Variable Alignment (Immediate) - -**Goal:** Ensure all services use the same environment variable names as defined in `pkg/config/types.go` - -**Tasks:** -1. Audit each service's env tags against pkg/config/types.go -2. Update service env tags to match centralized naming -3. Update Docker Compose files to use new env var names -4. Test each service independently - -**Example:** -```go -// Before (llm-api): -HTTPPort int `env:"HTTP_PORT"` - -// After (aligned with pkg/config/types.go): -HTTPPort int `env:"HTTP_PORT"` // OK Already matches! - -// Before (llm-api): -DBPostgresqlWriteDSN string `env:"DB_POSTGRESQL_WRITE_DSN,notEmpty"` - -// After (should use components): -// Build from POSTGRES_HOST, POSTGRES_PORT, POSTGRES_USER, etc. -``` - -### Phase 2: Configuration Bridge Pattern (Sprint 3-4) - -**Goal:** Create bridge functions that convert centralized config to service-specific config - -**Implementation:** -```go -// In services/llm-api/internal/config/bridge.go - -import centralconfig "github.com/janhq/jan-server/pkg/config" - -// FromCentralConfig converts pkg/config.Config to llm-api Config -func FromCentralConfig(central *centralconfig.Config) *Config { - return &Config{ - HTTPPort: central.Services.LLMAPI.HTTPPort, - DatabaseURL: buildDatabaseURL(central.Infrastructure.Database.Postgres), - //... map all fields - } -} -``` - -**Benefits:** -- Gradual migration (can still use env vars) -- Backward compatibility -- Clear mapping between old and new config - -### Phase 3: Direct Integration (Sprint 5+) - -**Goal:** Services directly use pkg/config types - -**Requires:** -1. Go workspace configuration or monorepo restructuring -2. Update Wire providers to inject centralconfig.Config -3. Remove service-specific Config structs -4. Update all service code to use central types - -## Current Status - -### Sprint 2 Complete OK -- pkg/config foundation built -- Precedence system (100-600) implemented -- All tests passing -- Documentation complete - -### Sprint 3 Next Steps - -**Option A: Environment Variable Alignment (Recommended for Sprint 3)** -- OK Low risk, immediate value -- OK No code restructuring needed -- OK Can be done per-service incrementally -- Tasks: - 1. Create env var mapping document - 2. Update docker compose.yml environment sections - 3. Update service env tags - 4. Test each service - -**Option B: Module Restructuring (Deferred to Sprint 4-5)** -- WARNING Requires Go workspace setup or monorepo migration -- WARNING Higher risk, more invasive -- WARNING Blocks other development during migration -- Tasks: - 1. Set up Go workspace in root - 2. Update all go.mod files - 3. Implement bridge pattern - 4. Migrate services one by one - 5. Comprehensive integration testing - -## Decision: Sprint 3 Scope - -**RECOMMENDATION:** Focus on Option A for Sprint 3 - -**Rationale:** -1. **Immediate Value:** Standardizing env vars provides immediate operational benefits -2. **Low Risk:** No code changes, only configuration alignment -3. **Foundation for Phase 2:** Makes bridge pattern easier in Sprint 4 -4. **Testable:** Can validate each service independently - -**Deliverables for Sprint 3:** -1. Environment variable mapping document -2. Updated docker compose.yml with aligned env vars -3. Service-by-service env var audit -4. Integration tests validating env var precedence - -## Implementation Plan - -### Task 3.1: Environment Variable Audit - -Create `docs/configuration/env-var-mapping.md` documenting: -- All env vars from pkg/config/types.go -- Current env vars in each service -- Mapping/migration needed -- Deprecation timeline - -### Task 3.2: Docker Compose Updates - -Update `docker compose.yml` and `infra/docker/` files to use: -- Standardized env var names -- config/environments/*.yaml for environment-specific overrides - -### Task 3.3: Service Validation - -For each service (llm-api, mcp-tools, media-api, response-api): -1. Update internal config env tags to match pkg/config -2. Run unit tests -3. Run integration tests -4. Verify in Docker environment - -### Task 3.4: Documentation - -1. Update service READMEs with new env var names -2. Create migration guide for operators -3. Document any breaking changes - -## Sprint 4+ Preview - -Once Sprint 3 (env var alignment) is complete, Sprint 4 can tackle: -- Go workspace setup -- Bridge pattern implementation -- Gradual service migration to use pkg/config directly - -This two-phase approach minimizes risk while delivering incremental value. diff --git a/apps/platform/content/docs/conventions/architecture-patterns.mdx b/apps/platform/content/docs/conventions/architecture-patterns.mdx deleted file mode 100644 index 177b78f0..00000000 --- a/apps/platform/content/docs/conventions/architecture-patterns.mdx +++ /dev/null @@ -1,140 +0,0 @@ ---- -title: "Architecture & Structure Conventions" ---- - -# Architecture & Structure Conventions - -> Use this file to understand how Jan Server is organised today. Every example below references the real repository structure under `services/`. - ---- - -## Repository Layout (Top Level) - -``` -jan-server/ -+-- tools/jan-cli/ # jan-cli sources + wrappers (jan-cli.sh / jan-cli.ps1) -+-- config/ # Shared configuration defaults and templates -+-- infra/docker/ # Compose fragments (infra, services, observability) -+-- docs/ # Documentation (guides, conventions, templates, etc.) -+-- k8s/ # Helm chart + Kubernetes manifests -+-- services/ -� +-- llm-api/ -� +-- media-api/ -� +-- response-api/ -� +-- mcp-tools/ -� +-- template-api/ -+-- tests/ # jan-cli api-test collections -+-- Makefile # Canonical automation entry point -+-- docker-compose.yml # Root compose file wired to profiles -+-- docker-compose.dev-full.yml # Dev-Full overrides (host routing) -``` - -Each service folder contains the same structure: - -``` -services// -+-- cmd/ -� +-- server/ # Service entrypoint -� +-- gormgen/ # (llm-api) schema generator -+-- config/ # Service-specific configuration helpers -+-- internal/ -� +-- domain/ # Business logic (no HTTP/DB imports) -� +-- infrastructure/ # Repositories, cache, provider clients -� +-- interfaces/httpserver/ # Gin routes, requests, responses, middlewares -+-- migrations/ # SQL migrations -+-- swagger/ or docs/swagger/ # Generated OpenAPI files -+-- scripts/ # Service utilities (optional) -+-- Makefile # Service-local helpers (e.g., `make gormgen`) -+-- go.mod / go.sum # Module definition -``` - -> Paths in the other convention documents are relative to `services//`. - ---- - -## Clean Architecture Layers - -``` -Interfaces (routes, cron, event consumers) - ? -Domain (entities, services, validation) - ? -Infrastructure (repositories, cache, providers) -``` - -**Rules:** -- Domain packages only import other domain packages plus injected interfaces (e.g., repository interfaces). -- Infrastructure implements those interfaces and may import external drivers (PostgreSQL, Redis, provider SDKs, etc.). -- Interfaces (HTTP) bind requests to domain services. Do not place business logic in Gin handlers. - ---- - -## File Placement Cheat Sheet - -| Task | Location | Example | -|------|----------|---------| -| New domain aggregate | `services//internal/domain//` | `services/llm-api/internal/domain/conversation/` | -| New HTTP endpoint | `services//internal/interfaces/httpserver/routes//` | `services/llm-api/internal/interfaces/httpserver/routes/v1/conversations/` | -| New schema/table | `services//internal/infrastructure/database/dbschema/` | -| Repository implementation | `services//internal/infrastructure/database/repository//` | -| Cache / provider client | `services//internal/infrastructure//` | -| Shared helper | `services//internal/utils//` | - -### Domain Entity Package - -``` -services//internal/domain// -+-- .go # Entity struct + methods -+-- service.go # Business logic / orchestrations -+-- filter.go # Query filters (optional) -+-- dto.go # Converters if needed -``` - -### Infrastructure Repository Package - -``` -services//internal/infrastructure/database/ -+-- dbschema/ # Schema structs + EtoD/DToE helpers -+-- repository/ -� +-- repo/ # Repository implementation -+-- gormgen/ # Generated query builders (llm-api) -``` - -### HTTP Interface Package - -``` -services//internal/interfaces/httpserver/ -+-- routes/v1// # Route registration + handlers -+-- requests// # Request DTOs + validation -+-- responses// # Response DTOs -+-- middlewares/ # Shared middleware -``` - ---- - -## When to Add New Packages - -1. **New domain concept** ? create `internal/domain/` with entity + service. -2. **New transport handler** ? add to `internal/interfaces/httpserver/routes/v1/` and create `requests/` and `responses/` entries as needed. -3. **New persistence logic** ? add schema file under `dbschema/` and repository under `repository/repo/`. Run `make gormgen` afterwards. -4. **New provider client** ? add package under `internal/infrastructure//` and inject through the service constructors. - ---- - -## Anti-Patterns To Avoid - -- **Direct DB access from handlers**: always go through domain services. -- **Fat handlers**: route handlers should validate input, call domain services, and return responses�nothing more. -- **Storing business logic in `internal/utils`**: keep helpers generic; domain rules belong in domain services. -- **Creating interfaces �just in case�**: only introduce an interface when multiple implementations exist or tests require it. - ---- - -## Quick Layer Checklist - -- Domain packages import only standard library and other domain packages. -- Infrastructure packages never import HTTP routes. -- Requests/responses convert to domain types immediately (`req.ToDomain()` / builders for responses). -- GORM pointer rules enforced in `dbschema/` structs. - -See `design-patterns.md` for concrete code samples and `workflow.md` for the commands that keep code generation and testing in sync. diff --git a/apps/platform/content/docs/conventions/conventions.mdx b/apps/platform/content/docs/conventions/conventions.mdx deleted file mode 100644 index 351f150c..00000000 --- a/apps/platform/content/docs/conventions/conventions.mdx +++ /dev/null @@ -1,103 +0,0 @@ ---- -title: "CONVENTIONS - Quick Reference" ---- - -# CONVENTIONS - Quick Reference - -> Start here for the current Jan Server standards. Detailed references live in the other files inside `docs/conventions/`. - ---- - -## Documentation Map - -| File | Purpose | -|------|---------| -| `conventions.md` (this file) | TL;DR and quick links | -| `architecture-patterns.md` | Repository and service layout patterns | -| `design-patterns.md` | Code-level guidance (DB, errors, entities) | -| `workflow.md` | Daily workflow: git, testing, deployments | - ---- - -## TL;DR Rules - -### Language & Tooling -- **Go version:** `1.25.0` (matches the root `go.mod`). Run `go fmt ./...` and `go test ./...` before committing. -- **Dependency hygiene:** `go mod tidy` inside the service you changed. - -### Architecture -- Each service lives under `services//` and follows the same structure (`cmd/`, `internal/`, `migrations/`, etc.). -- Clean architecture still applies: Interfaces (HTTP) ? Domain ? Infrastructure. Domain packages never import database or HTTP packages. - -### Database -- GORM zero-value issue still exists. Use pointer fields (`*bool`, `*float64`, etc.) in schema structs and convert to plain types in domain models. -- When schemas change, run `make gormgen` from the service directory (e.g., `cd services/llm-api && make gormgen`). - -### Error Handling -- Trigger point (repository) creates errors via `platformerrors.NewError()`. -- Handlers call `responses.HandleError()` so every response includes `requestID`. -- Never log secrets or the raw error from external providers. - -### Naming -- Exported symbols: `PascalCase`. Unexported: `camelCase`. -- Database columns: `snake_case`. -- Avoid stuttering (`provider.ID`, not `provider.ProviderID`). -- Avoid single word naming, must meaningful, easy to read and understand - -### Git & Commits -- Conventional commits: `feat:`, `fix:`, `docs:`, `test:`, `chore:`, etc. -- Branches: `type/short-description` (e.g., `feat/dev-full-refresh`). - -### Security -- Secrets only live in `.env`/environment variables. `.env` is created from `.env.template` via `make setup` and never committed. -- Kong + Keycloak handle auth; do not bypass JWT/API-key validation in services. - ---- - -## Common Commands - -```bash -# Setup & environments -make setup # Copy .env.template -> .env and docker/.env -make up-full # Start infra + APIs + MCP in Docker -make dev-full # Start Docker stack with host routing for native services -./jan-cli.sh dev run llm-api # Run a service on host (macOS/Linux) -.\jan-cli.ps1 dev run llm-api # Same on Windows - -# Monitoring & tooling -make monitor-up # Prometheus + Grafana + Jaeger -make monitor-clean # Stop monitoring and remove volumes - -# Testing -make test-all # Run every jan-cli api-test collection -make test-auth # Focused suite (see Makefile for others) -go test ./services/llm-api/... # Service-level unit tests - -# Code generation -(cd services/llm-api && make gormgen) # Regenerate GORM queries after schema changes -make swagger # Rebuild Swagger docs for all services - -# Database helpers -make db-console # Open psql shell inside api-db -make db-reset # Drop + recreate llm-api database -``` - ---- - -## Critical Checklist Before Pushing - -1. `go fmt ./...` in every service you touched. -2. `go test ./...` (unit) and `make test-all` or the relevant jan-cli api-test suites if you changed APIs. -3. `make swagger` if REST contracts changed. -4. `(cd services/ && make gormgen)` if DB schemas changed. -5. `.env`/secrets unchanged and never committed. -6. Conventional commit message, CI passes locally (`make up-full && make health-check`). - ---- - -## Need More Detail? -- **Structure & file placement:** `architecture-patterns.md` -- **Code patterns (DB, entities, errors):** `design-patterns.md` -- **Daily workflow (git, CI/CD, deployment):** `workflow.md` - -Always keep docs and commands in sync with the Makefile, jan-cli, and the actual service directories. If a command does not exist locally, update the documentation first. diff --git a/apps/platform/content/docs/conventions/design-patterns.mdx b/apps/platform/content/docs/conventions/design-patterns.mdx deleted file mode 100644 index 083b1f2d..00000000 --- a/apps/platform/content/docs/conventions/design-patterns.mdx +++ /dev/null @@ -1,646 +0,0 @@ ---- -title: "Code Patterns & Best Practices" ---- - -# Code Patterns & Best Practices - -> **When to read this:** Daily reference for AI agents and developers writing code. -> -> **For structure:** See [architecture-patterns.md](./architecture-patterns) -> **For workflow:** See [workflow.md](./workflow) -> **Quick reference:** See [conventions.md](./conventions) - ---- - -## Table of Contents - -1. [Database Patterns](#database-patterns) -2. [Error Handling](#error-handling) -3. [Domain Entity Creation](#domain-entity-creation) -4. [Performance Patterns](#performance-patterns) - -> **Path convention:** The examples below assume you are working inside a service directory such as `services/llm-api/`. Adjust paths accordingly for other services. - ---- - -## Database Patterns - -### GORM Zero-Value Handling CRITICAL - -**Problem:** GORM's `.Save()` silently skips fields with zero values (`false`, `0`, `0.0`) to avoid overwriting database data with uninitialized struct fields. - -**Solution:** Use pointer types for fields that may legitimately be set to zero values. - -```go -// Bad: Cannot set Enabled to false or Amount to 0.0 -type User struct { - BaseModel - Enabled bool `gorm:"not null;default:true"` - Amount float64 `gorm:"not null"` -} - -// Good: Use pointers for zero-affected fields -type User struct { - BaseModel - Enabled *bool `gorm:"not null;default:true"` - Amount *float64 `gorm:"not null"` -} - -// Conversion pattern in NewSchemaUser() - create pointer from value -func NewSchemaUser(u *user.User) *User { - enabled:= u.Enabled - amount:= u.Amount - return &User{ - Enabled: &enabled, // Always non-nil pointer - Amount: &amount, - } -} - -// Conversion pattern in EtoD() - dereference with nil-check -func (u *User) EtoD() *user.User { - enabled:= false // Default value - if u.Enabled != nil { - enabled = *u.Enabled - } - amount:= 0.0 // Default value - if u.Amount != nil { - amount = *u.Amount - } - return &user.User{ - Enabled: enabled, // Plain type in domain - Amount: amount, - } -} -``` - -**When to use pointers:** -- Boolean fields that need to be `false` (e.g., `Enabled`, `Active`, `IsPrivate`) -- Numeric fields that can be `0` or `0.0` (e.g., `Amount`, `Credits`) -- Fields that are always non-zero (e.g., IDs, timestamps) -- Counters that only increment (e.g., `ViewCount`) - -**Why this works:** `*bool` zero value is `nil`, so `&false` is NOT a zero value -> GORM updates it - -**Common scenarios fixed:** -- Disabling API keys (`Enabled = false`) -- Deactivating users/providers (`Active = false`) -- Recording $0.00 transactions (`Amount = 0.0`) -- Zero-credit operations (`Credits = 0`) - ---- - -### Schema Definition Pattern - -```go -// internal/infrastructure/database/dbschema/organization.go -type Organization struct { - BaseModel // Must include BaseModel (ID, CreatedAt, UpdatedAt, DeletedAt) - PublicID string `gorm:"size:64;not null;uniqueIndex"` - Name string `gorm:"size:255;not null"` - Active *bool `gorm:"not null;default:true;index"` // Pointer for zero-value -} - -func init() { - database.RegisterSchemaForAutoMigrate(Organization{}) -} - -// EtoD: Entity to Domain (method on schema struct) -func (e *Organization) EtoD() *domain.Organization { - if e == nil { - return nil - } - active:= true // Default - if e.Active != nil { - active = *e.Active - } - return &domain.Organization{ - ID: e.ID, - PublicID: e.PublicID, - Name: e.Name, - Active: active, - CreatedAt: e.CreatedAt, - UpdatedAt: e.UpdatedAt, - } -} - -// NewSchemaOrganization: Domain to Entity (package-level function) -func NewSchemaOrganization(d *domain.Organization) *Organization { - if d == nil { - return nil - } - active:= d.Active - return &Organization{ - BaseModel: BaseModel{ - ID: d.ID, - CreatedAt: d.CreatedAt, - UpdatedAt: d.UpdatedAt, - }, - PublicID: d.PublicID, - Name: d.Name, - Active: &active, - } -} -``` - -**Key points:** -- `EtoD()` is a method (has receiver) -- `NewSchema*()` is a function (no receiver) -- Always nil-check to prevent panics -- For pointers: create variable, then reference it - ---- - -### Repository Pattern - -```go -type OrganizationRepository struct { - db *transaction.Database -} - -// Create returns the created entity with generated fields -func (r *OrganizationRepository) Create(ctx context.Context, org *domain.Organization) (*domain.Organization, error) { - dbOrg:= dbschema.NewSchemaOrganization(org) - - if err:= r.db.GetQuery(ctx).Organization.WithContext(ctx).Create(dbOrg); err != nil { - return nil, platformerrors.NewError(ctx, platformerrors.LayerRepository, - platformerrors.ErrorTypeDatabaseError, "failed to create organization", err, - "c1d2e3f4-a5b6-4c7d-8e9f-0a1b2c3d4e5f") // Unique UUID - } - - return dbOrg.EtoD(), nil -} - -// FindByID uses GORM gen for type-safe queries -func (r *OrganizationRepository) FindByID(ctx context.Context, id string) (*domain.Organization, error) { - o:= r.db.GetQuery(ctx).Organization - dbOrg, err:= o.WithContext(ctx).Where(o.PublicID.Eq(id)).First() - - if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, platformerrors.NewError(ctx, platformerrors.LayerRepository, - platformerrors.ErrorTypeNotFound, "organization not found", err, - "d4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f8a") - } - return nil, platformerrors.NewError(ctx, platformerrors.LayerRepository, - platformerrors.ErrorTypeDatabaseError, "failed to find organization", err, - "e7f8a9b0-c1d2-4e3f-4a5b-6c7d8e9f0a1b") - } - - return dbOrg.EtoD(), nil -} - -// Update uses.Save() - works correctly with pointer types -func (r *OrganizationRepository) Update(ctx context.Context, org *domain.Organization) (*domain.Organization, error) { - dbOrg:= dbschema.NewSchemaOrganization(org) - - if err:= r.db.GetQuery(ctx).Organization.WithContext(ctx). - Where(r.db.GetQuery(ctx).Organization.ID.Eq(org.ID)). - Save(dbOrg); err != nil { - return nil, platformerrors.NewError(ctx, platformerrors.LayerRepository, - platformerrors.ErrorTypeDatabaseError, "failed to update organization", err, - "f0a1b2c3-d4e5-4f6a-7b8c-9d0e1f2a3b4c") - } - - return dbOrg.EtoD(), nil -} - -// List with filters and pagination -func (r *OrganizationRepository) List(ctx context.Context, filter *OrganizationFilter) ([]*domain.Organization, int64, error) { - o:= r.db.GetQuery(ctx).Organization - query:= o.WithContext(ctx) - - // Apply filters - if filter.Active != nil { - query = query.Where(o.Active.Is(*filter.Active)) - } - if filter.Name != nil { - query = query.Where(o.Name.Like("%" + *filter.Name + "%")) - } - - // Count total - total, err:= query.Count() - if err != nil { - return nil, 0, platformerrors.NewError(ctx, platformerrors.LayerRepository, - platformerrors.ErrorTypeDatabaseError, "failed to count", err, "uuid-here") - } - - // Apply pagination (cursor-based preferred) - if filter.Pagination != nil && filter.Pagination.LastID != "" { - query = query.Where(o.ID.Gt(filter.Pagination.LastID)) - } - if filter.Pagination != nil && filter.Pagination.Limit > 0 { - query = query.Limit(filter.Pagination.Limit) - } - - dbOrgs, err:= query.Find() - if err != nil { - return nil, 0, platformerrors.NewError(ctx, platformerrors.LayerRepository, - platformerrors.ErrorTypeDatabaseError, "failed to list", err, "uuid-here") - } - - // Convert to domain - orgs:= make([]*domain.Organization, len(dbOrgs)) - for i, dbOrg:= range dbOrgs { - orgs[i] = dbOrg.EtoD() - } - - return orgs, total, nil -} -``` - -**Filter signature:** -```go -type OrganizationFilter struct { - ID *uint - PublicID *string - Name *string - Active *bool - Pagination *PaginationFilter -} - -type PaginationFilter struct { - Limit int - LastID string // For cursor-based pagination -} -``` - ---- - -### GORM Gen Usage - -```bash -# After schema changes, regenerate queries -cd services/llm-api -make gormgen -``` - -**Generated queries** live in `services/llm-api/internal/infrastructure/database/gormgen/` - -**Type-safe queries:** -```go -// Good: Compile-time safe -o:= query.Use(db).Organization -orgs, err:= o.WithContext(ctx). - Where(o.Active.Is(true)). - Order(o.CreatedAt.Desc()). - Limit(100). - Find() - -// Bad: String-based (not type-safe) -db.Where("active = ?", true). - Order("created_at DESC"). - Find(&orgs) -``` - ---- - -### Transactions - -```go -// Use transaction wrapper -err:= r.db.Transaction(func(tx *gorm.DB) error { - // All operations in this function are transactional - if err:= tx.Create(&org).Error; err != nil { - return err // Rolls back - } - - if err:= tx.Create(&user).Error; err != nil { - return err // Rolls back - } - - return nil // Commits -}) -``` - ---- - -## Error Handling - -### Error Handling Philosophy - -**3-Layer Pattern:** -1. **Repository (trigger point):** Use `NewError()` with unique UUID -2. **Domain (business layer):** Use `AsError()` to add context OR pass through -3. **Route (HTTP layer):** Use `HandleError()` or `HandleNewError()` - -### Trigger Point Pattern (Repository) - -```go -func (r *UserRepository) FindByID(ctx context.Context, id string) (*domain.User, error) { - u:= query.Use(r.db.GetDB()).User - dbUser, err:= u.WithContext(ctx).Where(u.PublicID.Eq(id)).First() - - if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - // NewError at trigger point with unique UUID - return nil, platformerrors.NewError( - ctx, - platformerrors.LayerRepository, - platformerrors.ErrorTypeNotFound, - "user not found", - err, - "3e47b618-b750-4064-9b22-ece9e244019d", // Generate unique UUID - ) - } - return nil, platformerrors.NewError(ctx, platformerrors.LayerRepository, - platformerrors.ErrorTypeDatabaseError, "database query failed", err, - "7f29ac41-8d5e-4a73-b3c1-9e8f2d6a5c4b") // Different UUID per error - } - return dbUser.EtoD(), nil -} -``` - -**UUID Generation:** -```bash -# VS Code: Install "UUID Generator" by netcorext -# Command: Ctrl+Shift+P -> "Insert UUID" -# CLI: uuidgen -# Web: https://www.uuidgenerator.net/ -``` - -### Domain Layer Pattern - -```go -// Option 1: Add context with AsError() -func (s *UserService) GetUser(ctx context.Context, id string) (*User, error) { - user, err:= s.repo.FindByID(ctx, id) - if err != nil { - return nil, platformerrors.AsError(ctx, platformerrors.LayerDomain, err, - "failed to retrieve user") - } - return user, nil -} - -// Option 2: Pass through (no additional context needed) -func (s *UserService) GetUserSimple(ctx context.Context, id string) (*User, error) { - return s.repo.FindByID(ctx, id) // Just pass through -} -``` - -### Route Layer Pattern - -```go -// Use HandleError for errors from services -func (r *UserRoute) GetUser(c *gin.Context) { - id:= c.Param("id") - - user, err:= r.service.GetUser(c.Request.Context(), id) - if err != nil { - responses.HandleError(c, err) // Converts PlatformError to HTTP response - return - } - responses.Success(c, BuildUserResponse(user)) -} - -// Use HandleNewError for errors at route level -func (r *UserRoute) CreateUser(c *gin.Context) { - var req CreateUserRequest - if err:= c.ShouldBindJSON(&req); err != nil { - responses.HandleNewError(c, platformerrors.LayerRoute, - platformerrors.ErrorTypeValidation, "invalid request", err) - return - } - - user, err:= r.service.CreateUser(c.Request.Context(), req.ToDomain()) - if err != nil { - responses.HandleError(c, err) - return - } - responses.Success(c, BuildUserResponse(user)) -} -``` - ---- - -## Domain Entity Creation - -### Step-by-Step Pattern - -When creating a new entity (e.g., `organization`): - -#### 1. Domain Entity - -```go -// internal/domain/organization/organization.go -type Organization struct { - ID uint - PublicID string - Name string - Active bool - CreatedAt time.Time - UpdatedAt time.Time -} - -// Entity validates itself -func (o *Organization) Normalize() error { - o.Name = strings.TrimSpace(o.Name) - if o.Name == "" { - return errors.New("name required") - } - return nil -} -``` - -#### 2. Domain Service - -```go -// internal/domain/organization/organizationservice.go -type OrganizationService struct { - repo *organizationrepo.OrganizationRepository - cache *organizationcache.OrganizationCache -} - -// Work directly with domain entities -func (s *OrganizationService) Create(ctx context.Context, org *Organization) (*Organization, error) { - if err:= org.Normalize(); err != nil { - return nil, platformerrors.NewError(ctx, platformerrors.LayerDomain, - platformerrors.ErrorTypeValidation, "invalid organization", err, "uuid-here") - } - - created, err:= s.repo.Create(ctx, org) - if err != nil { - return nil, err // Already wrapped at repository - } - - return created, nil -} -``` - -#### 3. Database Schema - -See [Schema Definition Pattern](#schema-definition-pattern) above - -#### 4. Repository - -See [Repository Pattern](#repository-pattern) above - -#### 5. HTTP Route - -```go -// internal/interfaces/httpserver/routes/v1/management/organizations/route.go -type OrganizationRoute struct { - service *organization.OrganizationService -} - -func (r *OrganizationRoute) Create(c *gin.Context) { - var req CreateOrganizationRequest - if err:= c.ShouldBindJSON(&req); err != nil { - responses.HandleNewError(c, platformerrors.LayerRoute, - platformerrors.ErrorTypeValidation, "invalid request", err) - return - } - - org, err:= r.service.Create(c.Request.Context(), req.ToDomain()) - if err != nil { - responses.HandleError(c, err) - return - } - - responses.Success(c, BuildOrganizationResponse(org)) -} -``` - ---- - -## Performance Patterns - -### Avoid N+1 Queries - -```go -// Bad: N+1 query problem -users, _:= db.Find(&users) -for _, user:= range users { - db.First(&profile, "user_id = ?", user.ID) // Query per user! -} - -// Good: Preload relationships -db.Preload("Profile").Find(&users) - -// Good: Use joins for filtering -db.Joins("JOIN profiles ON profiles.user_id = users.id"). - Where("profiles.verified = ?", true). - Find(&users) -``` - -### Cursor-Based Pagination - -```go -// Good: Cursor-based (scales well) -u:= query.Use(db).User -users, err:= u.WithContext(ctx). - Where(u.ID.Gt(lastID)). // lastID from previous page - Limit(pageSize). - Find() - -// Acceptable for small datasets: Offset pagination -users, err:= u.WithContext(ctx). - Offset(page * pageSize). - Limit(pageSize). - Find() -``` - -### Caching Pattern - -```go -func (s *OrganizationService) GetByID(ctx context.Context, id string) (*Organization, error) { - // Try cache first - cacheKey:= fmt.Sprintf("org:%s", id) - if cached, err:= s.cache.Get(ctx, cacheKey); err == nil { - return cached, nil - } - - // Cache miss: fetch from DB - org, err:= s.repo.FindByID(ctx, id) - if err != nil { - return nil, err - } - - // Store in cache (fire and forget, don't block on cache errors) - go s.cache.Set(context.Background(), cacheKey, org, 5*time.Minute) - - return org, nil -} - -// Invalidate cache on updates -func (s *OrganizationService) Update(ctx context.Context, org *Organization) (*Organization, error) { - updated, err:= s.repo.Update(ctx, org) - if err != nil { - return nil, err - } - - // Invalidate cache - cacheKey:= fmt.Sprintf("org:%s", org.PublicID) - go s.cache.Delete(context.Background(), cacheKey) - - return updated, nil -} -``` - -### Context Timeouts - -```go -// Set timeouts for external calls -ctx, cancel:= context.WithTimeout(ctx, 10*time.Second) -defer cancel() - -resp, err:= httpClient.Get(ctx, url) -``` - -### Batch Operations - -```go -// Good: Batch insert -db.CreateInBatches(users, 100) // Insert in batches of 100 - -// Good: Bulk update with IN clause -u:= query.Use(db).User -u.WithContext(ctx). - Where(u.ID.In(ids...)). - Update(u.Active, false) -``` - ---- - -## Common Patterns Reference - -### Request -> Domain Conversion - -```go -type CreateOrganizationRequest struct { - Name string `json:"name" binding:"required"` -} - -func (r *CreateOrganizationRequest) ToDomain() *organization.Organization { - return &organization.Organization{ - Name: r.Name, - Active: true, // Default - } -} -``` - -### Domain -> Response Conversion - -```go -type OrganizationResponse struct { - ID string `json:"id"` - Name string `json:"name"` - Active bool `json:"active"` - CreateAt string `json:"created_at"` -} - -func BuildOrganizationResponse(org *organization.Organization) *OrganizationResponse { - return &OrganizationResponse{ - ID: org.PublicID, - Name: org.Name, - Active: org.Active, - CreatedAt: org.CreatedAt.Format(time.RFC3339), - } -} -``` - ---- - -**See also:** -- [architecture-patterns.md](./architecture-patterns) - Structure & layers -- [workflow.md](./workflow) - Git, testing, deployment -- [conventions.md](./conventions) - Quick TL;DR reference diff --git a/apps/platform/content/docs/conventions/meta.json b/apps/platform/content/docs/conventions/meta.json deleted file mode 100644 index aed41915..00000000 --- a/apps/platform/content/docs/conventions/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Conventions", - "pages": ["index", "conventions", "architecture-patterns", "design-patterns", "workflow"] -} diff --git a/apps/platform/content/docs/conventions/workflow.mdx b/apps/platform/content/docs/conventions/workflow.mdx deleted file mode 100644 index 42f2d59b..00000000 --- a/apps/platform/content/docs/conventions/workflow.mdx +++ /dev/null @@ -1,209 +0,0 @@ ---- -title: "Development Workflow & Process" ---- - -# Development Workflow & Process - -> Daily workflow for working on Jan Server. These steps reflect the multi-service layout (`services//`) and the Makefile targets that exist today. - ---- - -## Table of Contents - -1. [Environment & Local Setup](#environment--local-setup) -2. [Git Workflow](#git-workflow) -3. [Testing Strategy](#testing-strategy) -4. [Code Generation](#code-generation) -5. [Security Practices](#security-practices) -6. [Logging Standards](#logging-standards) -7. [Code Review Checklist](#code-review-checklist) -8. [Common Commands](#common-commands) -9. [Deployment Checklist](#deployment-checklist) - ---- - -## Environment & Local Setup - -```bash -# 1. Clone -git clone https://github.com/janhq/jan-server.git -cd jan-server - -# 2. Create .env and docker/.env, verify Docker, etc. -make setup - -# 3. Start full stack (PostgreSQL, Keycloak, Kong, APIs, MCP) -make up-full - -# 4. Check health -make health-check -``` - -### Dev-Full Hybrid Workflow - -Use dev-full when you want to run a service natively with IDE tooling while the rest stay in Docker. - -```bash -make dev-full # Boot stack with host.docker.internal routing -./jan-cli.sh dev run llm-api # macOS/Linux (stops container + runs host process) -.\jan-cli.ps1 dev run llm-api # Windows PowerShell -``` - -Stop dev-full with `make dev-full-stop` (containers paused) or `make dev-full-down` (containers removed). - -### Environment Variables - -- `.env` (created by `make setup`) is the single source of truth. Copy from `.env.template` and set secrets (HF token, SERPER key, etc.). -- `docker/.env` is automatically kept in sync so Docker Compose uses the same values. -- When running a service natively, pass the environment via `jan-cli dev run --env path/to/env` or export manually. - ---- - -## Git Workflow - -### Branches & Commits - -- Branch format: `type/short-description` (e.g., `feat/dev-full-refresh`). -- Use [Conventional Commits](https://www.conventionalcommits.org/) for messages (`feat:`, `fix:`, `docs:`, `test:`, `chore:`, `refactor:`, etc.). - -### Typical Flow - -```bash -git checkout -b feat/new-flow -# ...edit files... -go fmt ./... -go test ./services/llm-api/... -make test-all - -git add -git commit -m "feat(llm-api): add new flow" -git push origin feat/new-flow -``` - -### Pull Requests - -1. Keep PRs focused on one change. -2. Mention any manual testing performed (e.g., `make test-all`, `make dev-full`, curl commands). -3. Update documentation when commands/configuration change. -4. Wait for CI (GitHub Actions) to pass before merging. - ---- - -## Testing Strategy - -| Scope | Command | -|-------|---------| -| Unit tests (per service) | `go test ./services//...` | -| Full jan-cli api-test suite | `make test-all` | -| Focused integration suites | `make test-auth`, `make test-conversations`, `make test-media`, `make test-mcp-integration`, etc. | -| Health checks | `make health-check` | - -Guidelines: -- Run the service-level Go tests before committing. -- Run the relevant jan-cli api-test collection (or `make test-all`) when changing API routes, Kong config, or MCP tools. -- For MCP-only work, `make test-mcp-integration` plus manual curl checks against `http://localhost:8000/mcp`. - ---- - -## Code Generation - -| Task | Command | -|------|---------| -| Swagger docs (all services) | `make swagger` | -| GORM queries (llm-api) | `cd services/llm-api && make gormgen` | -| Other services | Add service-local generators if needed (follow llm-api example) | - -Run generators whenever you change schema structs or API contracts, then commit the generated files. - ---- - -## Security Practices - -- Secrets (`HF_TOKEN`, `SERPER_API_KEY`, `BACKEND_CLIENT_SECRET`, etc.) stay in `.env` only. -- Never log access tokens, API keys, or PII. Use structured logging fields (`logger.With(...)`) instead. -- Kong + Keycloak enforce JWT/API key validation�never bypass them in service routes. -- Use HTTPS when transmitting secrets externally; assume `.env` values will be different in production. - ---- - -## Logging Standards - -- Structured logs with `logger.With()` and key/value pairs. -- Include `requestID` (propagated via middleware) in every log and error response. -- Use log levels consistently: `Debug` for development noise, `Info` for state changes, `Warn` for recoverable issues, `Error` for failures. -- Do not log request/response bodies unless behind a debug flag. - ---- - -## Code Review Checklist - -**Architecture** -- Domain packages contain business rules only. -- Infrastructure code is injected and has no HTTP imports. -- Routes remain thin; DTOs convert to domain structs immediately. - -**Database** -- Schema structs use pointers for zero-value fields. -- `EtoD()` / `DtoE()` cover nil vs default conversion. -- Repositories rely on GORM-gen query builders. - -**Errors & Logging** -- Trigger points create `platformerrors.NewError()` instances with UUIDs. -- Errors bubble through `AsError()` and are rendered with `responses.HandleError()`. -- request IDs logged and returned. - -**Testing** -- Unit tests cover new code paths. -- Integration/jan-cli api-test suites updated if APIs change. -- Feature flags or env toggles documented. - -**Security** -- No credentials committed or logged. -- Input validation present on HTTP DTOs. -- Kong/Keycloak configs updated if auth flow changes. - -**Docs** -- README/guides updated when commands or endpoints change. -- Swagger re-generated for API changes. - ---- - -## Common Commands - -```bash -# Services & infrastructure -make up-full # Start everything via Docker -make down # Stop and remove containers (keeps volumes) -make dev-full # Start Docker stack with host routing -make dev-full-down # Remove dev-full containers -make monitor-up # Observability stack -make monitor-clean # Remove monitoring volumes - -# Database helpers -make db-console # psql shell into api-db -make db-reset # Drop & recreate llm-api database - -# Testing -make test-all # All jan-cli api-test suites -make test-auth # Focused collection -make test-mcp-integration - -# Lint/format (run inside the service module) -go fmt ./... -go test ./services//... -``` - ---- - -## Deployment Checklist - -- [ ] `go fmt ./...` -- [ ] `go test ./services//...` -- [ ] `make test-all` (or relevant suites) -- [ ] `make swagger` (if API contracts changed) -- [ ] `(cd services/llm-api && make gormgen)` (if schemas changed) -- [ ] `.env` changes documented but not committed -- [ ] Docker/Kubernetes manifests updated if ports/env vars changed -- [ ] PR reviewed and CI green - -Once everything passes, follow the platform release process (merge to `main`, tag if needed, then promote via the deployment pipeline). diff --git a/apps/platform/content/docs/guides/authentication.mdx b/apps/platform/content/docs/guides/authentication.mdx deleted file mode 100644 index 4a637db0..00000000 --- a/apps/platform/content/docs/guides/authentication.mdx +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: "Authentication & Gateway" ---- - -# Authentication & Gateway - -This guide describes the Kong + Keycloak solution that fronts every `/llm/*` request in Jan Server. The implementation uses Kong OSS plugins (`jwt` + `keycloak-apikey`) so the edge accepts Keycloak-issued JWTs or scoped API keys before requests reach the microservices. - -## 1. Architectural Overview - -- **Kong gateway** (`http://localhost:8000`) is the sole public endpoint for Jan Server. Every API (LLM, Response, Media, MCP, auth) is exposed through Kong routes that perform JWT/API-key validation, rate limiting, request transformation, and header sanitation. -- **Keycloak** (realm `jan`) issues OAuth2/OIDC tokens. Services and Kong both depend on the Keycloak JWKS endpoint (`http://keycloak:8085/realms/jan/protocol/openid-connect/certs`) for signature validation. -- **LLM API** is responsible for guest onboarding, API key lifecycle endpoints, and the `/auth/validate-api-key` callback consumed by the Kong plugin. -- **Custom auth plugin** (`keycloak-apikey`) replaces Kong consumers/credentials in DB-less mode by delegating API key validation to the service layer. - -## 2. Kong Authentication Flow - -| Plugin | Purpose | Key config | -| ------ | ------- | ---------- | -| `jwt` | Validates Keycloak JWTs | `key_claim_name: iss`, `claims_to_verify: ["exp","nbf"]`, `maximum_expiration: 3600`, `anonymous: kong-anon-jwt`, `secret_is_base64: false`, `run_on_preflight: false` | -| `keycloak-apikey` | Validates API keys via LLM API | `validation_url: http://llm-api:8080/auth/validate-api-key`, `hide_credentials: true`, `validation_timeout: 5000`, `run_on_preflight: false` | -| `request-termination` (anonymous fallback) | Returns 401 when neither plugin runs | - -The routes define **OR logic**: requests are accepted if either the JWT or API key plugin succeeds. Kong also injects `X-Auth-Method` (value `jwt` or `apikey`) and user context headers (`X-User-ID`, `X-User-Subject`, `X-User-Email`, `X-User-Username`) so downstream services know who authenticated the call. - -### Flowchart - -``` -Client - +--> Kong Gateway (`/llm/*`) - +-- JWT Plugin (Keycloak) - | +--> Valid token -> Add `X-Auth-Method: jwt`, inject user headers -> Upstream - +-- API Key Plugin (`keycloak-apikey`) - | +--> Forward `X-API-Key` to `llm-api/auth/validate-api-key` - | +--> LLM API hashes key, consults Keycloak -> Valid -> Inject headers + `X-Auth-Method: apikey` - +-- Request-termination (fallback) -> Return 401 -``` - -## 3. Guest Tokens - -- **Endpoint**: `POST /llm/auth/guest-login` exposed through Kong (`/llm/auth/guest-login` route). This endpoint creates a temporary Keycloak user and returns `access_token`, `refresh_token`, and metadata. Guest tokens are meant for quick local testing; they honor rate limits and expire around 5 minutes. -- **Temporary Email**: Guest users are automatically assigned a temporary email in the format `guest-{uuid}@temp.jan.ai` to satisfy Keycloak's email requirements. This temporary email is replaced with the real email when the guest account is upgraded via `POST /auth/upgrade`. -- **Usage**: Include `Authorization: Bearer ` on `/v1/*` calls or sent via Kong using `curl -X POST http://localhost:8000/llm/auth/guest-login`. Kong forwards the request to `llm-api` and enforces the auth plugin (JWT may succeed immediately after issuance). -- **Upgrade**: Call `POST /auth/upgrade` with the guest token to convert to a permanent account. The upgrade endpoint overwrites the temporary email with a real email and marks it as verified, and changes the `guest` attribute from `true` to `false`. -- **Refresh**: Call `/llm/auth/refresh-token` or rely on Kong's JWT verification for new tokens in production flows. - -## 4. API Key Lifecycle - -- **Format**: Keys use the `sk_` prefix plus 32 random characters. The shared secret is shown only once (on creation). Services store only the SHA-256 hash inside Keycloak user attributes and PostgreSQL (`api_keys` table from `000001_init_schema.up.sql`). -- **Endpoints** (require JWT auth): - - `POST /auth/api-keys` - Create a new API key tied to the authenticated user. - - `GET /auth/api-keys` - List active keys for the calling user. - - `DELETE /auth/api-keys/{id}` - Revoke a key. - - `POST /auth/validate-api-key` - Public validation endpoint called by Kong's plugin. -- **Validation Flow**: - 1. Kong receives `X-API-Key` from the client. - 2. `keycloak-apikey` calls `http://llm-api:8080/auth/validate-api-key`. - 3. LLM API hashes the key, compares it against Keycloak attributes, and responds with user data (or `401` when invalid). - 4. Kong injects user headers and marks the request as authenticated (can now enforce rate limits per consumer). - -## 5. Keycloak Integration Notes - -- **JWKS**: The Kong `jwt` plugin fetches the Keycloak JWKS manually (no dynamic JWKS refresh). Rotate Keycloak signing keys via a manual Kong restart or redeploy the gateway. -- **Admin API**: Credentials (JWT secrets) live only in the Kong Admin API and are never committed to Git. The gateway does not create consumers dynamically in DB-less mode, which keeps configuration declarative (`kong.yml`). -- **Guest users**: Each guest login request creates a temporary Keycloak user with a temporary email (`guest-{uuid}@temp.jan.ai`) and the `guest` attribute set to `true`. These users can be upgraded to permanent accounts via `/auth/upgrade`, which replaces the temporary email with a real one and toggles the `guest` flag to `false`. Upgrade and refresh flows use the same `jan` realm policies as regular users. - -## 6. Environment & Deployment Guidance - -- **Overlays**: Use environment-specific Kong overlays (`docker`, `k8s/jan-server/templates`, etc.) to toggle TLS verification (`ssl_verify: false` in development, `true` plus CA bundles in staging/prod). -- **Rate limiting**: Kong enforces per-IP limits at the gateway plus per-consumer bucketed limits where a consumer is resolved either from JWT claims (`iss` -> `keycloak-issuer`) or from API key metadata. -- **Plugin loading**: Custom `keycloak-apikey` code lives in `kong/plugins/keycloak-apikey/` (handler + schema + README). Compose mounts `../kong/plugins:/usr/local/kong/plugins:ro` and sets `KONG_PLUGINS: bundled,keycloak-apikey`. -- **Credentials**: The plugin uses `hide_credentials: true` so backend services never see the raw `X-API-Key`. - -## 7. Observability & Follow-up - -- **Metrics**: Expose plugin-specific stats for auth method usage and failure reasons. Consider adding Redis caching for `validate-api-key` responses to reduce latency. -- **Logging**: Kong logs record which plugin succeeded; look for `X-Auth-Method` in `request-transformer`-injected headers. -- **Tests**: jan-cli api-test suites verify `/auth/api-keys`, `/llm/auth/guest-login`, and the `validate-api-key` call. Run `make test-auth` in development. - -## 8. Security Hardening Summary - -- **Hashed secrets**: API key secrets are hashed with SHA-256 and stored inside Keycloak user attributes to avoid storing plaintext tokens. -- **Single-use visibility**: Keys are shown only once (creation response) to prevent accidental leaks. -- **Fallback response**: `request-termination` returns `401` when neither plugin authenticates, preventing unauthenticated requests from reaching services. -- **Anonymous consumer**: The `kong-anon-jwt` anonymous consumer is configured purely for the OR logic gate; it has no access beyond the gateway. - -This document replaces the implementation roadmap formerly captured in `auth-todo.md`. Keep it updated whenever you add authentication routes, adjust Kong plugins, or change Keycloak realms. diff --git a/apps/platform/content/docs/guides/background-mode.mdx b/apps/platform/content/docs/guides/background-mode.mdx deleted file mode 100644 index 2e6435b9..00000000 --- a/apps/platform/content/docs/guides/background-mode.mdx +++ /dev/null @@ -1,423 +0,0 @@ ---- -title: "Background Mode Implementation" ---- - -# Background Mode Implementation - -## Overview - -The Response API now supports OpenAI-compatible background mode for asynchronous response generation. This allows clients to submit long-running requests without holding open HTTP connections. - -## Architecture - -### Components - -1. **PostgreSQL-backed Queue**: Uses the `responses` table with `SELECT FOR UPDATE SKIP LOCKED` for reliable task distribution -2. **Worker Pool**: Fixed-size pool of background workers (default: 4) that poll for queued tasks -3. **Webhook Notifications**: HTTP POST callbacks when tasks complete or fail -4. **Graceful Cancellation**: Queued tasks can be cancelled before execution begins - -### Task Lifecycle - -``` -Client Request (background=true, store=true) - ↓ -Create Response (status=queued, queued_at=now) - ↓ -Return Response Immediately - ↓ -Worker Dequeues Task - ↓ -Mark Processing (status=in_progress, started_at=now) - ↓ -Execute LLM Orchestration - ↓ -Update Status (completed/failed, completed_at=now) - ↓ -Send Webhook Notification (async, non-blocking) -``` - -## API Usage - -### Creating a Background Response - -**Request:** -```http -POST /responses -Content-Type: application/json -Authorization: Bearer - -{ - "model": "gpt-4", - "input": "Write a long article about...", - "background": true, - "store": true, - "metadata": { - "webhook_url": "https://example.com/webhooks/responses" - } -} -``` - -**Response (201 Created):** -```json -{ - "id": "resp_abc123", - "object": "response", - "status": "queued", - "background": true, - "store": true, - "queued_at": "2024-01-15T10:30:00Z", - "created": "2024-01-15T10:30:00Z", - "metadata": { - "webhook_url": "https://example.com/webhooks/responses" - } -} -``` - -### Polling for Status - -**Request:** -```http -GET /responses/resp_abc123 -Authorization: Bearer -``` - -**Response (In Progress):** -```json -{ - "id": "resp_abc123", - "status": "in_progress", - "started_at": "2024-01-15T10:30:05Z", - ... -} -``` - -**Response (Completed):** -```json -{ - "id": "resp_abc123", - "status": "completed", - "output": "The article...", - "usage": { - "prompt_tokens": 150, - "completion_tokens": 500, - "total_tokens": 650 - }, - "started_at": "2024-01-15T10:30:05Z", - "completed_at": "2024-01-15T10:35:22Z", - ... -} -``` - -### Cancelling a Background Task - -**Request:** -```http -POST /responses/resp_abc123/cancel -Authorization: Bearer -``` - -**Response:** -```json -{ - "id": "resp_abc123", - "status": "cancelled", - "cancelled_at": "2024-01-15T10:31:00Z", - ... -} -``` - -**Cancellation Behavior:** -- If status is `queued`: Immediately marks cancelled, prevents worker pickup -- If status is `in_progress`: Marks cancelled, but task may complete normally (cooperative cancellation) -- If status is `completed` or `failed`: No-op, returns current state - -## Webhook Notifications - -### Webhook Payload (Completed) - -```json -{ - "id": "resp_abc123", - "event": "response.completed", - "status": "completed", - "output": "The response content...", - "metadata": {...}, - "completed_at": "2024-01-15T10:35:22Z" -} -``` - -### Webhook Payload (Failed) - -```json -{ - "id": "resp_abc123", - "event": "response.failed", - "status": "failed", - "error": { - "code": "execution_failed", - "message": "LLM provider timeout" - }, - "metadata": {...} -} -``` - -### Webhook Delivery - -- **Method**: HTTP POST -- **Content-Type**: `application/json` -- **Headers**: - - `User-Agent: jan-response-api/1.0` - - `X-Jan-Event: response.completed` (or `response.failed`) - - `X-Jan-Response-ID: resp_abc123` -- **Retries**: Up to 3 attempts with 2-second delays -- **Timeout**: 10 seconds per attempt -- **Non-blocking**: Webhook failures are logged but don't affect task completion - -## Configuration - -### Environment Variables - -```bash -# Background Task Processing -BACKGROUND_WORKER_COUNT=4 # Number of concurrent workers -BACKGROUND_TASK_TIMEOUT=600s # Max execution time per task -BACKGROUND_POLL_INTERVAL=2s # How often workers poll for tasks - -# Webhook Configuration -WEBHOOK_TIMEOUT=10s # HTTP request timeout -WEBHOOK_MAX_RETRIES=3 # Number of retry attempts -WEBHOOK_RETRY_DELAY=2s # Delay between retries -``` - -### Recommended Settings - -**Development:** -- `BACKGROUND_WORKER_COUNT=2` -- `BACKGROUND_TASK_TIMEOUT=300s` - -**Production:** -- `BACKGROUND_WORKER_COUNT=8` -- `BACKGROUND_TASK_TIMEOUT=600s` -- Monitor queue depth and adjust worker count as needed - -## Constraints - -1. **Store Requirement**: `background=true` requires `store=true` - - Returns `400 Bad Request` if violated - - Rationale: Background responses must be retrievable later - -2. **No Streaming**: Background mode never streams - - `stream` parameter is ignored for background tasks - - Rationale: Client receives response immediately, cannot consume stream - -3. **Task Timeout**: Tasks exceeding `BACKGROUND_TASK_TIMEOUT` are terminated - - Status marked as `failed` with timeout error - - Webhook notification sent - -## Database Schema - -### New Fields in `responses` Table - -```sql --- Indicates if response was created in background mode -background BOOLEAN NOT NULL DEFAULT FALSE; - --- Indicates if response should be stored (required for background) -store BOOLEAN NOT NULL DEFAULT FALSE; - --- Timestamp when task was queued -queued_at TIMESTAMP; - --- Timestamp when worker began processing -started_at TIMESTAMP; - --- Index for efficient queue queries -CREATE INDEX idx_responses_status ON responses(status) WHERE background = TRUE; -``` - -### Queue Query - -Workers use this query to dequeue tasks: - -```sql -SELECT * FROM responses -WHERE status = 'queued' - AND background = TRUE -ORDER BY queued_at ASC -LIMIT 1 -FOR UPDATE SKIP LOCKED; -``` - -## Monitoring - -### Key Metrics - -1. **Queue Depth**: Count of tasks with `status='queued'` - ```sql - SELECT COUNT(*) FROM responses WHERE status = 'queued' AND background = TRUE; - ``` - -2. **Average Processing Time**: - ```sql - SELECT AVG(EXTRACT(EPOCH FROM (completed_at - started_at))) - FROM responses - WHERE status IN ('completed', 'failed') - AND background = TRUE - AND started_at IS NOT NULL; - ``` - -3. **Worker Utilization**: - ```sql - SELECT COUNT(*) FROM responses WHERE status = 'in_progress' AND background = TRUE; - ``` - -4. **Failure Rate**: - ```sql - SELECT - COUNT(CASE WHEN status = 'failed' THEN 1 END) * 100.0 / COUNT(*) as failure_rate - FROM responses - WHERE background = TRUE AND status IN ('completed', 'failed'); - ``` - -### Logging - -Workers log structured events: - -```json -{ - "level": "info", - "component": "worker", - "worker_id": 2, - "response_id": "resp_abc123", - "user_id": "user_xyz", - "model": "gpt-4", - "message": "processing background task" -} -``` - -## Error Handling - -### Common Errors - -| Error | HTTP Status | Description | -|-------|-------------|-------------| -| Missing Store | 400 | `background=true` without `store=true` | -| Task Timeout | 500 | Task exceeded `BACKGROUND_TASK_TIMEOUT` | -| LLM Provider Error | 500 | Upstream LLM API failure | -| Tool Execution Error | 500 | MCP tool call failed | - -### Recovery - -- **Transient Failures**: Tasks remain queued, workers retry automatically -- **Persistent Failures**: Status marked `failed`, error details in response -- **Webhook Failures**: Logged but don't block task completion - -## Testing - -### jan-cli api-test Collection - -Run the Postman collection for background mode: - -```bash -jan-cli api-test run tests/postman/responses-background-webhook.json \ - --environment tests/postman/environments/local.json \ - --delay-request 1000 \ - --timeout-request 60000 -``` - -### Manual Testing - -1. **Create Background Task**: - ```bash - curl -X POST http://localhost:8082/responses \ - -H "Content-Type: application/json" \ - -d '{ - "model": "gpt-4", - "input": "Count to 10 slowly", - "background": true, - "store": true, - "metadata": {"webhook_url": "https://webhook.site/unique-id"} - }' - ``` - -2. **Poll Status**: - ```bash - curl http://localhost:8082/responses/resp_abc123 - ``` - -3. **Cancel Task**: - ```bash - curl -X POST http://localhost:8082/responses/resp_abc123/cancel - ``` - -## Migration Guide - -### Upgrading Existing Systems - -1. **Database Migration**: Run migrations to add new columns -2. **Configuration**: Set environment variables for workers -3. **Deployment**: Rolling update (workers start automatically) -4. **Verification**: Check worker logs for startup - -### Backward Compatibility - -- Synchronous mode (`background=false`) unchanged -- Existing endpoints and behavior preserved -- Optional feature, no breaking changes - -## Troubleshooting - -### Queue Stuck - -**Symptom**: Tasks remain in `queued` status - -**Check**: -1. Are workers running? Check logs for "worker started" -2. Database connection healthy? -3. Any database locks? Check `pg_locks` - -**Fix**: -```bash -# Restart workers -docker restart response-api -``` - -### Webhooks Not Delivered - -**Symptom**: No webhook received despite completed task - -**Check**: -1. Is `webhook_url` in metadata? -2. Is webhook endpoint reachable? -3. Check logs for "webhook notification failed" - -**Fix**: -- Verify webhook URL is correct and accessible -- Check firewall/network rules -- Webhook failures don't affect task completion, manual retry needed - -### High Queue Depth - -**Symptom**: Growing number of queued tasks - -**Check**: -1. Worker utilization (should be near `BACKGROUND_WORKER_COUNT`) -2. Average processing time increasing? -3. LLM provider throttling? - -**Fix**: -```bash -# Increase workers -export BACKGROUND_WORKER_COUNT=8 -docker restart response-api -``` - -## Future Enhancements - -- [ ] Redis-backed queue for higher throughput -- [ ] Priority queuing (high/low priority tasks) -- [ ] Dead letter queue for failed tasks -- [ ] Webhook retry with exponential backoff -- [ ] Prometheus metrics export -- [ ] Grafana dashboard templates diff --git a/apps/platform/content/docs/guides/conversation-management.mdx b/apps/platform/content/docs/guides/conversation-management.mdx deleted file mode 100644 index a4759f37..00000000 --- a/apps/platform/content/docs/guides/conversation-management.mdx +++ /dev/null @@ -1,471 +0,0 @@ ---- -title: "Conversation Management Guide" ---- - -# Conversation Management Guide - -Complete guide for managing conversations, including creation, organization, deletion, and sharing. - -## Overview - -Conversations are the core of Jan Server - they store your chat history and context. This guide covers: - -- Creating and organizing conversations -- Managing conversation history and messages -- Deleting conversations (single and bulk) -- Sharing conversations and messages -- Organizing conversations in projects - -## Quick Start - -### Create a Conversation - -```bash -curl -X POST http://localhost:8000/v1/conversations \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "title": "My First Conversation", - "project_id": "optional-project-id" - }' -``` - -### Send a Message - -```bash -curl -X POST http://localhost:8000/v1/conversations/conv_123/items \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "role": "user", - "content": "Hello, how are you?" - }' -``` - -### Delete a Conversation - -```bash -curl -X DELETE http://localhost:8000/v1/conversations/conv_123 \ - -H "Authorization: Bearer " -``` - -## Creating & Organizing Conversations - -### Create a New Conversation - -**POST** `/v1/conversations` - -Create a new conversation for a fresh discussion topic. - -**Request:** -```bash -curl -X POST http://localhost:8000/v1/conversations \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "title": "Project Planning", - "project_id": "proj_abc123" - }' -``` - -**Response:** -```json -{ - "id": "conv_123", - "title": "Project Planning", - "project_id": "proj_abc123", - "created_at": "2025-12-23T10:00:00Z", - "updated_at": "2025-12-23T10:00:00Z" -} -``` - -### List Your Conversations - -**GET** `/v1/conversations` - -List all conversations (paginated). - -**Query Parameters:** -- `limit` - Results per page (default: 20, max: 100) -- `after` - Pagination cursor for next page -- `project_id` - Filter by project (optional) - -**Request:** -```bash -curl -H "Authorization: Bearer " \ - "http://localhost:8000/v1/conversations?limit=10&project_id=proj_abc" -``` - -**Response:** -```json -{ - "data": [ - { - "id": "conv_123", - "title": "Project Planning", - "created_at": "2025-12-23T10:00:00Z", - "item_count": 12 - } - ], - "next_after": "conv_456" -} -``` - -### Get Conversation Details - -**GET** `/v1/conversations/{conv_id}` - -Retrieve full conversation with all messages. - -**Request:** -```bash -curl -H "Authorization: Bearer " \ - http://localhost:8000/v1/conversations/conv_123 -``` - -**Response:** -```json -{ - "id": "conv_123", - "title": "Project Planning", - "created_at": "2025-12-23T10:00:00Z", - "items": [ - { - "id": "item_1", - "role": "user", - "content": "Let's plan our project", - "created_at": "2025-12-23T10:05:00Z" - }, - { - "id": "item_2", - "role": "assistant", - "content": "Great! Let me help you...", - "created_at": "2025-12-23T10:05:30Z" - } - ] -} -``` - -### Update Conversation Title - -**PATCH** `/v1/conversations/{conv_id}` - -Update conversation metadata. - -**Request:** -```bash -curl -X PATCH http://localhost:8000/v1/conversations/conv_123 \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "title": "Updated Project Title" - }' -``` - -## Message Management - -### Send a Message - -**POST** `/v1/conversations/{conv_id}/items` - -Add a message to a conversation. - -**Request:** -```bash -curl -X POST http://localhost:8000/v1/conversations/conv_123/items \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "role": "user", - "content": "What are the next steps?", - "call_id": "optional-external-id" - }' -``` - -**Response:** -```json -{ - "id": "item_5", - "role": "user", - "content": "What are the next steps?", - "created_at": "2025-12-23T10:10:00Z" -} -``` - -### Edit a Message - -**PUT** `/v1/conversations/{conv_id}/items/{item_id}/edit` - -Modify an existing message (useful for regenerating AI responses). - -**Request:** -```bash -curl -X PUT http://localhost:8000/v1/conversations/conv_123/items/item_2/edit \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "content": "Actually, let me rephrase that..." - }' -``` - -### Regenerate AI Response - -**POST** `/v1/conversations/{conv_id}/items/{item_id}/regenerate` - -Ask the AI to regenerate a response (create a new version). - -**Request:** -```bash -curl -X POST http://localhost:8000/v1/conversations/conv_123/items/item_2/regenerate \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "model": "claude-3-5-sonnet" - }' -``` - -### Delete a Message - -**DELETE** `/v1/conversations/{conv_id}/items/{item_id}` - -Remove a message from a conversation. - -**Request:** -```bash -curl -X DELETE http://localhost:8000/v1/conversations/conv_123/items/item_2 \ - -H "Authorization: Bearer " -``` - -## Conversation Deletion - -### Delete Single Conversation - -**DELETE** `/v1/conversations/{conv_id}` - -Permanently delete a conversation and all its messages. - -**Request:** -```bash -curl -X DELETE http://localhost:8000/v1/conversations/conv_123 \ - -H "Authorization: Bearer " -``` - -**Response:** `204 No Content` - -### Bulk Delete Conversations - -**POST** `/v1/conversations/bulk-delete` - -Delete multiple conversations at once. - -**Request:** -```bash -curl -X POST http://localhost:8000/v1/conversations/bulk-delete \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "conversation_ids": ["conv_123", "conv_456", "conv_789"] - }' -``` - -**Response:** -```json -{ - "deleted_count": 3, - "failed_count": 0, - "failed_ids": [] -} -``` - -## Sharing Conversations - -### Share a Conversation (Create Link) - -**POST** `/v1/conversations/{conv_id}/share` - -Create a shareable link to your conversation. - -**Request:** -```bash -curl -X POST http://localhost:8000/v1/conversations/conv_123/share \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "expires_in": 86400, - "read_only": true - }' -``` - -**Response:** -```json -{ - "share_id": "share_abc123", - "url": "http://localhost:8000/conversations/share/share_abc123", - "expires_at": "2025-12-24T10:00:00Z", - "read_only": true -} -``` - -### Share a Single Message - -**POST** `/v1/conversations/{conv_id}/items/{item_id}/share` - -Create a shareable link to a specific message. - -**Request:** -```bash -curl -X POST http://localhost:8000/v1/conversations/conv_123/items/item_5/share \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "expires_in": 3600, - "include_context": true - }' -``` - -**Response:** -```json -{ - "share_id": "msg_share_xyz789", - "url": "http://localhost:8000/conversations/share/msg_share_xyz789", - "expires_at": "2025-12-23T11:00:00Z" -} -``` - -### Revoke Share Link - -**DELETE** `/v1/conversations/{conv_id}/share/{share_id}` - -Disable a previously shared link. - -**Request:** -```bash -curl -X DELETE http://localhost:8000/v1/conversations/conv_123/share/share_abc123 \ - -H "Authorization: Bearer " -``` - -## Project Organization - -Conversations are organized in projects for better management. - -### Create a Project - -**POST** `/v1/projects` - -Create a new project to organize related conversations. - -**Request:** -```bash -curl -X POST http://localhost:8000/v1/projects \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "title": "My AI Research", - "description": "Conversations about machine learning" - }' -``` - -**Response:** -```json -{ - "id": "proj_abc123", - "title": "My AI Research", - "created_at": "2025-12-23T10:00:00Z" -} -``` - -### Add Conversation to Project - -Create conversations with a project: - -```bash -curl -X POST http://localhost:8000/v1/conversations \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "title": "Research Question 1", - "project_id": "proj_abc123" - }' -``` - -Or update existing conversation: - -```bash -curl -X PATCH http://localhost:8000/v1/conversations/conv_123 \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "project_id": "proj_abc123" - }' -``` - -### List Project Conversations - -**GET** `/v1/projects/{project_id}/conversations` - -List all conversations in a project. - -**Request:** -```bash -curl -H "Authorization: Bearer " \ - http://localhost:8000/v1/projects/proj_abc123/conversations -``` - -## Python Examples - -## JavaScript Examples - -### Share a Conversation - -```javascript -const token = 'your_token_here'; -const headers = { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' -}; - -const convId = 'conv_123'; - -const response = await fetch( - `http://localhost:8000/v1/conversations/${convId}/share`, - { - method: 'POST', - headers, - body: JSON.stringify({ - expires_in: 86400, - read_only: true - }) - } -); - -const share = await response.json(); -console.log(`Share link: ${share.url}`); -``` - -## Best Practices - -1. **Organize with Projects**: Group related conversations in projects for easy navigation -2. **Use Meaningful Titles**: Give conversations descriptive titles (AI auto-titles if you don't) -3. **Regular Cleanup**: Delete old conversations you no longer need -4. **Share with Caution**: Review conversation content before sharing links -5. **Archive Instead**: Export conversations before deletion if you want to keep records -6. **Use Message IDs**: Reference `call_id` for external system integration - -## Limitations & Known Issues - -- Conversations limited to 10,000 messages per conversation -- Bulk delete limited to 100 conversations per request -- Share links are public (anyone with the link can view) -- Deleted conversations cannot be recovered - -## Related Documentation - -- [LLM API Reference](../api/llm-api/) - Complete API endpoints -- [Chat Completions](../api/llm-api/README#chat-completions) - Send messages with AI responses -- [User Settings](./user-settings-personalization) - Personalize responses -- [Projects Management](../api/llm-api/README#projects) - Organize conversations - ---- - -**Last Updated**: December 23, 2025 -**Compatibility**: Jan Server v0.0.14+ diff --git a/apps/platform/content/docs/guides/deployment.mdx b/apps/platform/content/docs/guides/deployment.mdx deleted file mode 100644 index 647d4a86..00000000 --- a/apps/platform/content/docs/guides/deployment.mdx +++ /dev/null @@ -1,732 +0,0 @@ ---- -title: "Deployment Guide" ---- - -# Deployment Guide - -Comprehensive guide for deploying Jan Server to various environments. - -## Table of Contents - -- [Overview](#overview) -- [Prerequisites](#prerequisites) -- [Deployment Options](#deployment-options) - - [Kubernetes (Recommended)](#kubernetes-recommended) - - [Docker Compose](#docker compose) - - [Hybrid Mode](#hybrid-mode) -- [Environment Configuration](#environment-configuration) -- [Security Considerations](#security-considerations) -- [Monitoring and Observability](#monitoring-and-observability) - -## Overview - -Jan Server supports multiple deployment strategies to accommodate different use cases: - -| Environment | Use Case | Orchestrator | Recommended For | -|-------------|----------|--------------|-----------------| -| **Kubernetes** | Production, Staging | Kubernetes/Helm | Scalable production deployments | -| **Docker Compose** | Development, Testing | Docker Compose | Local development and testing | -| **Hybrid Mode** | Development | Native + Docker | Fast iteration and debugging | - -## Prerequisites - -### All Deployments - -- Docker 24+ and Docker Compose V2 -- PostgreSQL 18+ (managed or in-cluster) -- Redis 7+ (managed or in-cluster) -- S3-compatible storage (for media-api) - -### Kubernetes Deployments - -- Kubernetes 1.27+ -- Helm 3.12+ -- kubectl configured -- Sufficient cluster resources (see [Resource Requirements](#resource-requirements)) - -## Deployment Options - -### Kubernetes (Recommended) - -Kubernetes deployment uses Helm charts for full orchestration and scalability. - -#### 1. Development (Minikube) - -For local development and testing: - -```bash -# Prerequisites -minikube start --cpus=4 --memory=8192 --driver=docker - -# Build and load images -cd services/llm-api && go mod tidy && cd ../.. -cd services/media-api && go mod tidy && cd ../.. -cd services/mcp-tools && go mod tidy && cd ../.. - -docker build -t jan/llm-api:latest -f services/llm-api/Dockerfile . -docker build -t jan/media-api:latest -f services/media-api/Dockerfile . -docker build -t jan/mcp-tools:latest -f services/mcp-tools/Dockerfile . - -# Load images into minikube -minikube image load jan/llm-api:latest jan/media-api:latest jan/mcp-tools:latest -minikube image load quay.io/keycloak/keycloak:24.0.5 -minikube image load bitnami/postgresql:latest bitnami/redis:latest - -# Deploy -cd k8s -helm install jan-server ./jan-server \ - --namespace jan-server \ - --create-namespace - -# Create databases -kubectl exec -n jan-server jan-server-postgresql-0 -- bash -c "PGPASSWORD=postgres psql -U postgres << 'EOF' -CREATE USER media WITH PASSWORD 'media'; -CREATE DATABASE media_api OWNER media; -CREATE USER keycloak WITH PASSWORD 'keycloak'; -CREATE DATABASE keycloak OWNER keycloak; -EOF" - -# Verify deployment -kubectl get pods -n jan-server - -# Access services -kubectl port-forward -n jan-server svc/jan-server-llm-api 8080:8080 -curl http://localhost:8080/healthz -``` - -**Complete guide:** See [k8s/SETUP.md](../../k8s/SETUP) - -#### 2. Cloud Kubernetes (AKS/EKS/GKE) - -For production cloud deployments: - -```bash -# Option A: With cloud-managed databases (recommended) -helm install jan-server ./jan-server \ - --namespace jan-server \ - --create-namespace \ - --set postgresql.enabled=false \ - --set redis.enabled=false \ - --set global.postgresql.host=your-managed-postgres.cloud \ - --set global.redis.host=your-managed-redis.cloud \ - --set ingress.enabled=true \ - --set ingress.className=nginx \ - --set ingress.hosts[0].host=jan.yourdomain.com \ - --set llmApi.autoscaling.enabled=true \ - --set llmApi.replicaCount=3 \ - --set llmApi.image.pullPolicy=Always \ - --set mediaApi.image.pullPolicy=Always \ - --set mcpTools.image.pullPolicy=Always - -# Option B: With in-cluster databases -helm install jan-server ./jan-server \ - --namespace jan-server \ - --create-namespace \ - --set postgresql.persistence.enabled=true \ - --set postgresql.persistence.size=50Gi \ - --set postgresql.persistence.storageClass=gp3 \ - --set redis.master.persistence.enabled=true \ - --set ingress.enabled=true \ - --set llmApi.autoscaling.enabled=true -``` - -**Configuration guide:** See [k8s/README.md](../../k8s/) - -#### 3. On-Premises Kubernetes - -For on-premises production: - -```bash -# Use production values with external databases -helm install jan-server ./jan-server \ - --namespace jan-server \ - --create-namespace \ - --values ./jan-server/values-production.yaml \ - --set postgresql.enabled=false \ - --set redis.enabled=false \ - --set global.postgresql.host=postgres.internal \ - --set global.redis.host=redis.internal -``` - -### Docker Compose - -For local development and integration testing. - -#### Development Mode - -```bash -# Start infrastructure only (PostgreSQL, Keycloak, Kong) -make up-infra - -# With API services (llm-api, media-api, response-api) -make up-api - -# With MCP services (mcp-tools, vector-store) -make up-mcp - -# Full stack with Kong + APIs + MCP -make up-full - -# With GPU inference (local vLLM) -make up-vllm-gpu -``` - -**Complete guide:** See [Development Guide](./development) - -#### Testing Environment - -```bash -cp .env.template .env # ensure a clean env file -# Edit .env and set: COMPOSE_PROFILES=infra,api,mcp - -make up-full # start stack under test -make test-all # run jan-cli api-test suites -``` - -### Hybrid Mode - -For fast iteration during development: - -```bash -make dev-full # start stack with host routing - -# Replace a service with a host-native process -./jan-cli.sh dev run llm-api # macOS/Linux -.\jan-cli.ps1 dev run llm-api # Windows PowerShell - -# Stop dev-full when done -make dev-full-stop # keep containers -make dev-full-down # remove containers -``` - -**Complete guide:** See [Development Guide - Dev-Full Mode](development#dev-full-mode-hybrid-debugging) - -## Environment Configuration - -### Required Environment Variables - -#### LLM API - -```bash -# Database -DB_POSTGRESQL_WRITE_DSN=postgres://jan_user:jan_password@localhost:5432/jan_llm_api?sslmode=disable - -# Keycloak/Auth -KEYCLOAK_BASE_URL=http://localhost:8085 -BACKEND_CLIENT_ID=llm-api -BACKEND_CLIENT_SECRET=your-secret -CLIENT=jan-client - -# Provider toggles -VLLM_ENABLED=true -VLLM_PROVIDER_URL=http://localhost:8101/v1 -REMOTE_LLM_ENABLED=false -REMOTE_LLM_PROVIDER_URL= -REMOTE_API_KEY= -JAN_PROVIDER_CONFIGS=true -JAN_PROVIDER_CONFIG_SET=default -HTTP_PORT=8080 -LOG_LEVEL=debug -``` - -#### Media API - -```bash -# Database -DB_POSTGRESQL_WRITE_DSN=postgres://media:media@localhost:5432/media_api?sslmode=disable - -# S3 Storage (Required - AWS Standard Naming) -MEDIA_S3_ENDPOINT=https://s3.amazonaws.com -MEDIA_S3_REGION=us-east-1 -MEDIA_S3_BUCKET=your-bucket -MEDIA_S3_ACCESS_KEY_ID=your-access-key-id -MEDIA_S3_SECRET_ACCESS_KEY=your-secret-access-key -MEDIA_S3_USE_PATH_STYLE=false - -# Server -MEDIA_API_PORT=8285 -LOG_LEVEL=info -``` - -#### MCP Tools - -```bash -# Server -HTTP_PORT=8091 -LOG_LEVEL=info - -# Optional providers -EXA_API_KEY=your-exa-key -BRAVE_API_KEY=your-brave-key -``` - -### Configuration Files - -Environment-specific configuration files in `config/`: - -- `defaults.env` - Default values for all environments -- `development.env` - Local development settings -- `testing.env` - Test environment settings -- `production.env.example` - Production template (copy and customize) -- `secrets.env.example` - Secrets template (never commit actual secrets) - -## Multi-vLLM Instance Deployment (High Availability) - -For production deployments requiring high availability and load-balanced inference across multiple vLLM instances. - -### Overview - -Jan Server supports running multiple vLLM instances with **automatic round-robin load balancing**. This enables: - -- **High Availability**: Continue operating if one vLLM instance fails -- **Scalability**: Distribute inference load across multiple GPUs/servers -- **Flexible Resource Allocation**: Deploy vLLM instances independently - -### Architecture - -``` -LLM API (load balancer) - ├── vLLM Instance 1 (Port 8101) - ├── vLLM Instance 2 (Port 8102) - └── vLLM Instance 3 (Port 8103) -``` - -The LLM API uses round-robin scheduling to distribute requests across instances. - -### Deployment Steps - -#### 1. Configure Multiple vLLM Instances - -Add provider instances in `config/defaults.yaml`: - -```yaml -providers: - - vendor: vllm - enabled: true - endpoints: - - name: vllm-instance-1 - base_url: http://localhost:8101/v1 - api_key: "" - - name: vllm-instance-2 - base_url: http://localhost:8102/v1 - api_key: "" - - name: vllm-instance-3 - base_url: http://localhost:8103/v1 - api_key: "" -``` - -#### 2. Start Multiple vLLM Instances via Docker - -Create a `docker-compose.vllm-multi.yml`: - -```yaml -version: '3.8' - -services: - vllm-1: - image: vllm/vllm-openai:latest - container_name: vllm-instance-1 - environment: - - VLLM_API_KEY=${VLLM_API_KEY:-} - - VLLM_SERVED_MODEL_NAME=meta-llama/Llama-2-7b-hf - ports: - - "8101:8000" - volumes: - - vllm-cache-1:/root/.cache - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: 1 - capabilities: [gpu] - networks: - - jan-network - restart: unless-stopped - - vllm-2: - image: vllm/vllm-openai:latest - container_name: vllm-instance-2 - environment: - - VLLM_API_KEY=${VLLM_API_KEY:-} - - VLLM_SERVED_MODEL_NAME=meta-llama/Llama-2-7b-hf - ports: - - "8102:8000" - volumes: - - vllm-cache-2:/root/.cache - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: 1 - capabilities: [gpu] - networks: - - jan-network - restart: unless-stopped - - vllm-3: - image: vllm/vllm-openai:latest - container_name: vllm-instance-3 - environment: - - VLLM_API_KEY=${VLLM_API_KEY:-} - - VLLM_SERVED_MODEL_NAME=meta-llama/Llama-2-7b-hf - ports: - - "8103:8000" - volumes: - - vllm-cache-3:/root/.cache - deploy: - resources: - reservations: - devices: - - driver: nvidia - count: 1 - capabilities: [gpu] - networks: - - jan-network - restart: unless-stopped - -volumes: - vllm-cache-1: - vllm-cache-2: - vllm-cache-3: - -networks: - jan-network: - external: true -``` - -Start the instances: - -```bash -# Create shared network -docker network create jan-network - -# Start multi-vLLM stack -docker compose -f docker-compose.vllm-multi.yml up -d - -# Verify instances are healthy -curl http://localhost:8101/health -curl http://localhost:8102/health -curl http://localhost:8103/health -``` - -#### 3. Start LLM API with Multi-vLLM Configuration - -```bash -# Set environment to use multiple vLLM endpoints -export VLLM_ENABLED=true -export VLLM_PROVIDER_URL=http://localhost:8101/v1,http://localhost:8102/v1,http://localhost:8103/v1 - -# Start LLM API -docker compose -f docker-compose.yml -f infra/docker/services-api.yml up -d llm-api -``` - -Or in `docker-compose.yml`, set the environment variable: - -```yaml -services: - llm-api: - environment: - VLLM_ENABLED: "true" - VLLM_PROVIDER_URL: "http://vllm-1:8000/v1,http://vllm-2:8000/v1,http://vllm-3:8000/v1" -``` - -#### 4. Kubernetes Deployment (Helm) - -For Kubernetes, deploy vLLM instances as separate StatefulSets: - -```bash -# Create a values override file: k8s/values-multi-vllm.yaml -cat > k8s/values-multi-vllm.yaml << 'EOF' -vllmInstances: - enabled: true - instances: 3 - resources: - requests: - memory: "24Gi" - nvidia.com/gpu: "1" - limits: - memory: "32Gi" - nvidia.com/gpu: "1" - -llmApi: - env: - VLLM_ENABLED: "true" - # Load balancer will handle round-robin - VLLM_PROVIDER_URL: "http://vllm-0:8000/v1,http://vllm-1:8000/v1,http://vllm-2:8000/v1" - replicaCount: 2 -EOF - -# Deploy with multi-vLLM configuration -helm install jan-server ./jan-server \ - --namespace jan-server \ - --create-namespace \ - --values k8s/values-multi-vllm.yaml -``` - -### Load Balancing Strategy - -The LLM API implements **round-robin load balancing** across configured vLLM endpoints: - -1. **Request Distribution**: Each request is routed to the next vLLM instance in sequence -2. **Failover**: If a vLLM instance is unavailable, requests retry on the next instance -3. **Health Checks**: Periodic health checks verify instance availability - -### Monitoring Multi-vLLM Setup - -Monitor the vLLM instances and load balancing: - -```bash -# Check individual vLLM instance stats -curl http://localhost:8101/stats -curl http://localhost:8102/stats -curl http://localhost:8103/stats - -# Monitor LLM API logs for load balancing -docker logs -f llm-api | grep "vllm\|provider\|load" - -# Check request distribution across instances -docker stats vllm-instance-1 vllm-instance-2 vllm-instance-3 -``` - -### Troubleshooting Multi-vLLM - -#### Uneven Load Distribution - -If requests aren't evenly distributed: - -1. **Check instance health**: Verify all instances respond to health checks -2. **Review logs**: Check for error-based failover patterns -3. **Restart instances**: Clear any stuck states with rolling restarts - -```bash -# Rolling restart (minimizes downtime) -for i in 1 2 3; do - echo "Restarting vllm-instance-$i..." - docker restart vllm-instance-$i - sleep 30 # Wait for recovery -done -``` - -#### Instance Connection Failures - -```bash -# Test connectivity from LLM API container -docker exec llm-api curl http://vllm-instance-1:8000/health -docker exec llm-api curl http://vllm-instance-2:8000/health -docker exec llm-api curl http://vllm-instance-3:8000/health - -# Check Docker network connectivity -docker network inspect jan-network -``` - -#### Memory/GPU Issues - -If instances are running out of memory: - -1. **Reduce model size**: Use a smaller quantized model -2. **Reduce batch size**: Set `--max-model-len` in vLLM -3. **Add more instances**: Distribute load across more nodes -4. **Scale vertically**: Upgrade to GPUs with more VRAM - -```bash -# Update vLLM docker-compose to use smaller model -docker compose -f docker-compose.vllm-multi.yml down -# Edit docker-compose.vllm-multi.yml, change VLLM_SERVED_MODEL_NAME -docker compose -f docker-compose.vllm-multi.yml up -d -``` - -### Performance Tuning - -For optimal multi-vLLM performance: - -| Setting | Recommendation | Notes | -|---------|-----------------|-------| -| **Instances** | 2-4 per operator | Balance cost vs redundancy | -| **Batch Size** | 1-4 per instance | Depends on VRAM available | -| **Model Size** | 7B or smaller | For multi-instance on typical GPUs | -| **Tensor Parallelism** | Enabled if multi-GPU per instance | Reduces latency | -| **Quantization** | 8-bit or GPTQ | Reduces VRAM usage | - -### Cost Optimization - -Multi-vLLM enables several cost optimizations: - -- **Spot Instances**: Use cheaper spot GPUs with fast failover -- **Mixed Hardware**: Use different GPU types for different model sizes -- **Autoscaling**: Add/remove instances based on load -- **Batch Processing**: Queue requests to maximize GPU utilization - -## Security Considerations - -### Production Checklist - -- [ ] **Secrets Management** - - Use external secrets operator (e.g., AWS Secrets Manager, Azure Key Vault) - - Never commit secrets to version control - - Rotate credentials regularly - -- [ ] **Network Security** - - Enable network policies to restrict pod-to-pod communication - - Use TLS for all external endpoints - - Configure ingress with proper SSL certificates - -- [ ] **Authentication** - - Change default Keycloak admin password - - Configure proper realm settings - - Enable token exchange for client-to-client auth - -- [ ] **Database Security** - - Use managed database services when possible - - Enable SSL/TLS connections - - Implement backup and disaster recovery - -- [ ] **Pod Security** - - Apply pod security standards (restricted profile) - - Use non-root containers - - Enable security context constraints - -### Example: External Secrets - -```bash -# Install external-secrets operator -helm repo add external-secrets https://charts.external-secrets.io -helm install external-secrets external-secrets/external-secrets \ - --namespace external-secrets-system \ - --create-namespace - -# Create SecretStore for AWS Secrets Manager -kubectl apply -f - < - -# Describe pod for events -kubectl describe pod -n jan-server -``` - -#### Database Connection Failures - -```bash -# Verify PostgreSQL is running -kubectl exec -n jan-server jan-server-postgresql-0 -- psql -U postgres -c '\l' - -# Check database exists -kubectl exec -n jan-server jan-server-postgresql-0 -- psql -U postgres -c '\l' | grep media_api - -# Test connection from service pod -kubectl exec -n jan-server -- nc -zv jan-server-postgresql 5432 -``` - -#### Image Pull Failures - -For minikube: -```bash -# Verify images are loaded -minikube image ls | grep jan/ - -# Reload if missing -minikube image load jan/llm-api:latest -``` - -For production: -```bash -# Check image pull policy -kubectl get deployment -n jan-server jan-server-llm-api -o yaml | grep pullPolicy - -# Should be "Always" or "IfNotPresent" for registry images -``` - -## Related Documentation - -- [Kubernetes Setup Guide](../../k8s/SETUP) - Complete k8s deployment steps -- [Kubernetes Configuration](../../k8s/) - Helm chart configuration reference -- [Development Guide](./development) - Local development setup -- [Development Guide](./development) - Native service execution and dev-full mode -- [Monitoring Guide](./monitoring) - Observability setup -- [Architecture Overview](../architecture/) - System architecture - -## Support - -For additional help: -- Review [Getting Started](../quickstart) -- Check [Troubleshooting Guide](./troubleshooting) -- See [Architecture Documentation](../architecture/) diff --git a/apps/platform/content/docs/guides/development.mdx b/apps/platform/content/docs/guides/development.mdx deleted file mode 100644 index b6d68287..00000000 --- a/apps/platform/content/docs/guides/development.mdx +++ /dev/null @@ -1,352 +0,0 @@ ---- -title: "Development Guide" ---- - -# Development Guide - -How to set up, run, and iterate on Jan Server locally. This guide covers the full Docker stack, dev-full mode (hybrid debugging), and native service execution. - -## Table of Contents - -- [Development Guide](#development-guide) - - [Table of Contents](#table-of-contents) - - [Prerequisites](#prerequisites) - - [Quick Start](#quick-start) - - [Access Points](#access-points) - - [Project Layout](#project-layout) - - [Development Workflows](#development-workflows) - - [Full Docker Stack (default)](#full-docker-stack-default) - - [Dev-Full Mode (Hybrid Debugging)](#dev-full-mode-hybrid-debugging) - - [Why Use Dev-Full Mode?](#why-use-dev-full-mode) - - [Quick Start](#quick-start-1) - - [How It Works](#how-it-works) - - [Running Services Natively in Dev-Full Mode](#running-services-natively-in-dev-full-mode) - - [Workflow](#workflow) - - [Environment Variables for Hybrid Mode](#environment-variables-for-hybrid-mode) - - [Monitoring in Dev-Full Mode](#monitoring-in-dev-full-mode) - - [Running Services Natively](#running-services-natively) - - [Configuration](#configuration) - - [Database \& Tooling](#database--tooling) - - [Testing](#testing) - - [IDE Integration](#ide-integration) - - [Troubleshooting \& Next Steps](#troubleshooting--next-steps) - - [Common Issues](#common-issues) - - [Health Checks and Monitoring](#health-checks-and-monitoring) - - [Cleanup](#cleanup) - -## Prerequisites - -Install these before running any commands: - -- **Docker Desktop 24+** with Docker Compose V2 -- **GNU Make** (built in on macOS/Linux, install via Chocolatey/Brew on Windows) -- **Go 1.21+** (only required for native/hybrid execution or when editing Go code) - -> Tip: `make setup` uses `jan-cli dev setup` to verify Docker, copy `.env.template` to `.env`, and create `docker/.env` automatically. - -## Quick Start - -```bash -# 1. Clone and enter the repo -git clone https://github.com/janhq/jan-server.git -cd jan-server - -# 2. Create .env, docker/.env, and Docker networks -make setup - -# 3. Start the full stack (infra + APIs + MCP + optional vLLM) -make up-full - -# 4. Verify everything is healthy -make health-check - -# 5. Tail logs or iterate -make logs # all containers -docker compose ps # status -``` - -- **Stop containers**: `make down` -- **Remove volumes**: `make down-clean` -- **Restart a single service**: `make restart-api`, `make restart-kong`, `make restart-keycloak` - -## Access Points - -| Service | URL | Notes | -|---------|-----|-------| -| Kong Gateway | http://localhost:8000 | Single entry point for all APIs | -| LLM API | http://localhost:8080 | OpenAI-compatible API, `/healthz` for checks | -| Response API | http://localhost:8082 | Multi-step orchestration | -| Media API | http://localhost:8285 | File upload/management service | -| MCP Tools | http://localhost:8091 | Native MCP tool bridge | -| Memory Tools | http://localhost:8090 | Semantic memory service | -| Realtime API | http://localhost:8186 | WebRTC session management | -| Keycloak | http://localhost:8085 | Admin/Admin in development | -| PostgreSQL | localhost:5432 | Database user `jan_user` / password from `.env` | -| Grafana | http://localhost:3331 | Start with `make monitor-up` | -| Prometheus | http://localhost:9090 | Monitoring profile | -| Jaeger | http://localhost:16686 | Tracing profile | - -## Project Layout - -``` -jan-server/ -+-- services/ # llm-api, media-api, response-api, mcp-tools, template-api -+-- tools/jan-cli/ # jan-cli sources (`./jan-cli.sh`, `.\jan-cli.ps1` wrappers) -+-- pkg/config/ # Single source of truth for config defaults and schema -+-- infra/docker/ # Compose fragments (infrastructure, services, dev-full, observability) -+-- docker compose.yml # Root compose file (includes infra/docker/*.yml via profiles) -+-- docker compose.dev-full.yml # Extra compose overrides for dev-full -+-- kong/ # Gateway configuration (kong.yml + kong-dev-full.yml) -+-- docs/ # Documentation (guides, architecture, configuration) -+-- Makefile # Canonical automation entry point -+-- .env.template # Copy to .env and edit per environment -``` - -## Development Workflows - -Jan Server supports three development modes: -1. **Full Docker Stack** - All services in containers (parity with CI/production) -2. **Dev-Full Mode** - Hybrid approach with infrastructure in Docker and optional native service execution -3. **Native Execution** - Run services directly on host without dev-full scaffolding - -### Full Docker Stack (default) - -```bash -make up-full # start everything defined by COMPOSE_PROFILES in .env -make logs # follow logs for every service -make logs-api # only API services -make logs-mcp # MCP stack -make down # stop and remove containers -``` - -Use this mode for integration testing and parity with CI. `COMPOSE_PROFILES` controls which Compose profiles (`infra,api,mcp,full`) load—edit `.env` if you want to disable GPU/vLLM locally. - -### Dev-Full Mode (Hybrid Debugging) - -**Dev-full mode is the officially supported hybrid workflow.** It starts every dependency in Docker, configures Kong with `host.docker.internal` upstreams, and lets you replace any service with a native process via `jan-cli dev run `. - -#### Why Use Dev-Full Mode? - -- Fast iteration without rebuilding Docker images -- Debug with breakpoints while Kong continues to enforce plugins and auth -- Keep PostgreSQL, Keycloak, Kong, and monitoring inside Docker for parity -- Instant rollback: stop your host process and Docker takes over again - -#### Quick Start - -```bash -make setup # once per machine -make dev-full # start infra + APIs + MCP with host routing -``` - -After the stack is up you can replace a service: - -```bash -./jan-cli.sh dev run llm-api # macOS/Linux -.\jan-cli.ps1 dev run llm-api # Windows PowerShell -``` - -`jan-cli dev run` stops the matching container, loads environment variables from `.env` (override with `--env`), and runs `go run ./cmd/server` inside `services/`. - -#### How It Works - -`make dev-full`: -- Loads `.env` and copies it to `infra/docker/.env` via `ensure-docker-env` -- Runs `docker compose -f docker-compose.yml -f docker-compose.dev-full.yml --profile full up -d` -- Prints URLs for PostgreSQL, Keycloak, Kong, and every API/MCP service -- Keeps the `jan-network`/`jan-monitoring` networks around for fast restarts - -Kong's dual-target upstreams (from `kong/kong-dev-full.yml`): -```yaml -upstreams: - - name: llm-api-upstream - targets: - - target: llm-api:8080 - - target: host.docker.internal:8080 - healthchecks: - active: - http_path: /healthz -``` - -When you stop the Docker container, Kong automatically fails over to the host target. When your host process stops responding to `/healthz`, traffic returns to Docker. - -#### Running Services Natively in Dev-Full Mode - -| Service | Port | Command | -|---------|------|---------| -| LLM API | 8080 | `jan-cli dev run llm-api` | -| Media API | 8285 | `jan-cli dev run media-api` | -| Response API | 8082 | `jan-cli dev run response-api` | -| MCP Tools | 8091 | `jan-cli dev run mcp-tools` | -| Memory Tools | 8090 | `jan-cli dev run memory-tools` | -| Realtime API | 8186 | `jan-cli dev run realtime-api` | - -**Options:** -- Use `--build` to compile before running (`jan-cli dev run llm-api --build`) -- Pass `--env config/hybrid.env` if you keep a dedicated env file for host processes -- To hand control back to Docker, stop the host process and run `docker compose start ` - -#### Workflow - -1. **Start dev-full mode** - ```bash - make dev-full - ``` - -2. **Replace a service with native execution** - ```bash - ./jan-cli.sh dev run llm-api # macOS/Linux - .\jan-cli.ps1 dev run llm-api # Windows PowerShell - ``` - -3. **Iterate and debug** - - Launch Delve: `dlv debug ./cmd/server --headless --listen=:2345` - - Use `air` for hot reload inside `services/` - - Observe requests through Kong at http://localhost:8000 exactly as clients would - -4. **Hand control back to Docker** - - Stop your host process (Ctrl+C) - - Restart the container if needed: `docker compose start llm-api` - -5. **Stop dev-full** when done: - ```bash - make dev-full-stop # keep containers - make dev-full-down # remove containers - ``` - -#### Environment Variables for Hybrid Mode - -| Variable | Purpose | -|----------|---------| -| `DB_POSTGRESQL_WRITE_DSN` | PostgreSQL connection string. Use `localhost` when running on host | -| `KEYCLOAK_BASE_URL` / `ISSUER` / `JWKS_URL` | Auth endpoints (use `http://localhost:8085`) | -| `HTTP_PORT` | Local service port (8080, 8082, 8285, 8091, etc.) | -| `LOG_LEVEL` / `LOG_FORMAT` | Logging controls | -| `MCP_*` / `SEARXNG_URL` / `VECTOR_STORE_URL` | Tool integrations for mcp-tools | -| `OTEL_*` | Telemetry export (set `OTEL_ENABLED=true` to emit traces) | - -Need a dedicated hybrid env file? Create `config/hybrid.env`, copy values from `.env`, then run `jan-cli dev run llm-api --env config/hybrid.env`. - -#### Monitoring in Dev-Full Mode - -You can bring up observability while using dev-full: - -```bash -make monitor-up # Prometheus + Grafana + Jaeger -make monitor-logs # follow collector/datasource logs -``` - -Those containers watch the same `jan-network`, so traces and metrics include both Docker and host services (as long as you set `OTEL_ENABLED=true`). - -### Running Services Natively - -You can run a service completely outside Docker by providing the same environment variables from `.env`: - -```bash -# Example for llm-api -docker compose up -d api-db keycloak kong # ensure infra is running -cd services/llm-api -export DB_POSTGRESQL_WRITE_DSN="postgres://jan_user:${POSTGRES_PASSWORD}@localhost:5432/jan_llm_api?sslmode=disable" -export KEYCLOAK_BASE_URL="http://localhost:8085" -export JWKS_URL="http://localhost:8085/realms/jan/protocol/openid-connect/certs" -export ISSUER="http://localhost:8085/realms/jan" -export HTTP_PORT=8080 -export LOG_LEVEL=debug - -go run ./cmd/server -``` - -> Windows users can run `.\jan-cli.ps1 dev run llm-api --env .env` to load variables automatically. - -## Configuration - -- Copy `.env.template` to `.env` (or run `make setup`) and edit secrets like `HF_TOKEN`, `SERPER_API_KEY`, and `POSTGRES_PASSWORD` -- `make setup` also writes `docker/.env`, so Compose and jan-cli use the same values -- `pkg/config/defaults.yaml` is the canonical configuration, generated from Go structs in `pkg/config/types.go` -- Helpful jan-cli commands: - -```bash -jan-cli config validate --file config/defaults.yaml -jan-cli config show --path services.llm-api -jan-cli config export --format env --output config/generated.env -``` - -## Database & Tooling - -```bash -make db-migrate # Apply Go migrations for llm-api -make db-reset # Drop + recreate tables (uses docker compose) -make db-console # Opens psql inside the api-db container - -# Direct Docker examples -docker compose logs api-db -psql "postgres://jan_user:${POSTGRES_PASSWORD}@localhost:5432/jan_llm_api?sslmode=disable" -``` - -For backups and restores use `make db-backup` / `make db-restore`. The Makefile targets wrap `docker compose` so they work on Windows, macOS, and Linux. - -## Testing - -- **Full integration suite**: `make test-all` (runs every Postman collection listed in the Makefile) -- **Focused suites**: `make test-auth`, `make test-conversations`, `make test-response`, `make test-media`, `make test-mcp-integration`, `make test-e2e` -- **Unit tests**: run them from each service directory (`go test ./...`) - -See [Testing Guide](./testing) for platform details, CI coverage, and troubleshooting tips. - -## IDE Integration - -- VS Code launch configurations can depend on a task that runs `make dev-full` -- After the task finishes, `jan-cli dev run ` is a good preLaunchCommand -- Debuggers simply connect to the local port (Kong still listens on 8000) -- Hot reload tools (`air`, `reflex`, etc.) live inside `services/` - -Example `.vscode/launch.json` for debugging llm-api: -```json -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Debug LLM API", - "type": "go", - "request": "launch", - "mode": "debug", - "program": "${workspaceFolder}/services/llm-api/cmd/server", - "envFile": "${workspaceFolder}/.env", - "preLaunchTask": "dev-full-start" - } - ] -} -``` - -## Troubleshooting & Next Steps - -### Common Issues - -| Symptom | Fix | -|---------|-----| -| Kong keeps hitting the Docker container | Ensure the host service listens on the same port and returns 200 on `/healthz` | -| Service cannot reach PostgreSQL | Update `DB_POSTGRESQL_WRITE_DSN` to use `localhost` instead of `api-db` when running on host | -| Environment variables missing | Pass `--env .env` (default) or a custom env file to `jan-cli dev run` | -| Port already in use | Stop the other listener or change `HTTP_PORT` before running the host service | -| Want to debug multiple services | Run `jan-cli dev run ...` in multiple terminals; each command stops its corresponding container first | - -### Health Checks and Monitoring - -1. `make health-check` - verifies infrastructure, API, MCP, and optional services -2. `make logs` or `docker compose logs ` - inspect failures quickly -3. `make restart-kong` / `make restart-keycloak` - common fixes for gateway/auth issues -4. `make monitor-up` - bring up Grafana/Prometheus/Jaeger if you need observability while debugging - -### Cleanup - -```bash -make dev-full-stop # stop containers but keep them around -make dev-full-down # stop + remove containers -make down # standard docker workflow stop -make down-clean # remove volumes and networks for pristine state -``` - -Need more help? Review [Testing](./testing), [Troubleshooting](./troubleshooting), [Monitoring](./monitoring), and the configuration docs under `docs/configuration/`. - - - diff --git a/apps/platform/content/docs/guides/index.mdx b/apps/platform/content/docs/guides/index.mdx deleted file mode 100644 index 1abe7614..00000000 --- a/apps/platform/content/docs/guides/index.mdx +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: "Guides" ---- - -# Guides - -Comprehensive how-to guides for working with Jan Server. For incident response and on-call steps, see [../runbooks/README.md](../runbooks/). - -## Available Guides - -### Development -- **[Development Guide](./development)** - Complete development workflow, setup, and best practices -- **[Development Guide](./development)** - Full Docker, dev-full (hybrid), and native execution modes -- **[Testing Guide](./testing)** - Unit tests, integration tests, and testing best practices - -### Operations (how-to) -- **[Monitoring](./monitoring)** - Observability, metrics, traces, and dashboards -- **[Deployment](./deployment)** - Kubernetes, Docker Compose, and hybrid deployment strategies -- **[Troubleshooting](./troubleshooting)** - Common issues and solutions (links out to runbooks where applicable) - -### Special Topics -- **[MCP Testing](./mcp-testing)** - Testing MCP (Model Context Protocol) integration - -## Quick Links - -### For Developers -| Task | Guide | -|------|-------| -| Setup local environment | [Development Guide](./development) | -| Run services natively | [Development Guide - Dev-Full Mode](development#dev-full-mode-hybrid-debugging) | -| Write and run tests | [Testing Guide](./testing) | -| Debug issues | [Troubleshooting](./troubleshooting) | - -### For DevOps -| Task | Guide | -|------|-------| -| Deploy to production | [Deployment Guide](./deployment) | -| Setup monitoring | [Monitoring](./monitoring) | -| Troubleshoot issues | [Troubleshooting](./troubleshooting) (see runbooks for on-call steps) | - -### For QA -| Task | Guide | -|------|-------| -| Run integration tests | [Testing Guide](./testing) | -| Test MCP tools | [MCP Testing](./mcp-testing) | - -## Common Tasks - -### Development Workflow -```bash -# 1. Setup development environment (.env + infra/docker/.env) -make setup - -# 2. Start everything in Docker -make up-full - -# 3. Switch a service to native mode (optional) -docker compose stop llm-api -./jan-cli.sh dev run llm-api # macOS/Linux -.\jan-cli.ps1 dev run llm-api # Windows - -# 4. Run automated tests -make test-all # jan-cli api-test integration suites -go test ./services/llm-api/... # Unit tests from source -``` - -Use [Development Guide](./development) for the complete workflow including full Docker, dev-full (hybrid), and native execution modes. - -### Testing Workflow - -```bash -# Integration tests (runs all Postman collections) -make test-all - -# Specific test suites -make test-auth -make test-conversations -make test-mcp - -# Unit tests from source -go test ./... -``` - -See [Testing Guide](./testing) for details. - -### Monitoring Setup - -```bash -# Start monitoring stack -make monitor-up - -# Access dashboards -# - Grafana: http://localhost:3331 -# - Prometheus: http://localhost:9090 -# - Jaeger: http://localhost:16686 - -# View logs -make monitor-logs -``` - -See [Monitoring Guide](./monitoring) for details. - -## Getting Help - -Each guide includes: -- Step-by-step instructions -- Code examples -- Common pitfalls -- Troubleshooting tips -- Related resources - -### Need More Help? - -- Check the [Troubleshooting Guide](./troubleshooting) -- Review [Architecture Documentation](../architecture/) -- See [API Reference](../api/) -- Ask in [GitHub Discussions](https://github.com/janhq/jan-server/discussions) - ---- - -**Back to**: [Documentation Home](../) | **Next**: Choose a guide above diff --git a/apps/platform/content/docs/guides/jan-cli.mdx b/apps/platform/content/docs/guides/jan-cli.mdx deleted file mode 100644 index 2aa0b6b2..00000000 --- a/apps/platform/content/docs/guides/jan-cli.mdx +++ /dev/null @@ -1,798 +0,0 @@ ---- -title: "Jan CLI - Complete Guide" ---- - -# Jan CLI - Complete Guide - -**Last Updated**: January 2025 -**Status**: Production Ready OK -**Version**: 1.0.0 - -Complete documentation for the Jan CLI tool - installation, usage, commands, and technical details. - ---- - -## Table of Contents - -1. [Overview](#overview) -2. [Quick Start](#quick-start) -3. [Installation](#installation) -4. [Commands Reference](#commands-reference) -5. [Configuration Management](#configuration-management) -6. [Service Operations](#service-operations) -7. [Development Tools](#development-tools) -8. [Troubleshooting](#troubleshooting) -9. [Shell Completion](#shell-completion) -10. [Technical Details](#technical-details) - ---- - -## Overview - -Jan CLI is the official command-line interface for Jan Server, providing unified access to: - -- **Configuration Management** - Validate, export, and inspect configuration -- **Service Operations** - List services, view logs, check status -- **Development Tools** - Setup environment, scaffold services -- **Shell Completion** - Auto-completion for all major shells - -Built with [Cobra framework](https://github.com/spf13/cobra), the industry standard used by kubectl, docker, and github CLI. - -### Key Features - -- OK **Unified Interface** - Single command for all Jan Server operations -- OK **Professional Structure** - Industry-standard Cobra framework -- OK **Extensible** - Easy to add new commands -- OK **Well-Documented** - Comprehensive help and examples -- OK **Cross-Platform** - Works on Windows, Linux, macOS -- OK **Shell Completion** - Bash, Zsh, Fish, PowerShell support - ---- - -## Quick Start - -### Install Globally (Recommended) - -```bash -# From project root -make cli-install -``` - -This will: -1. Build the `jan-cli` binary -2. Install to your user's local bin directory -3. Display PATH setup instructions - -**Installation Locations:** -- **Linux/macOS:** `~/bin/jan-cli` -- **Windows:** `%USERPROFILE%\bin\jan-cli.exe` - -### Add to PATH - -**Windows (PowerShell):** -```powershell -# Temporary (current session) -$env:PATH += ";$env:USERPROFILE\bin" - -# Permanent (add to PowerShell profile) -notepad $PROFILE -# Add this line: -$env:PATH += ";$env:USERPROFILE\bin" -``` - -**Linux/macOS (Bash/Zsh):** -```bash -# Add to ~/.bashrc or ~/.zshrc -export PATH="$PATH:$HOME/bin" - -# Reload your shell -source ~/.bashrc # or source ~/.zshrc -``` - -### Verify Installation - -```bash -jan-cli --version -# Output: jan-cli version 1.0.0 - -jan-cli --help -# Output: Full help text with all commands -``` - -### First Commands - -```bash -# List all services -jan-cli service list - -# Validate configuration -jan-cli config validate - -# Show help for any command -jan-cli config --help -``` - ---- - -## Installation - -### Method 1: Global Installation (Recommended) - -Use the Makefile target to build and install `jan-cli`: - -```bash -# From project root -make cli-install -``` - -**What it does:** -1. Builds the binary with `go build` -2. Creates `~/bin` or `%USERPROFILE%\bin` if needed -3. Copies binary to bin directory -4. Sets execute permissions (Unix) -5. Checks if bin is in PATH -6. Shows PATH setup instructions if needed - -**After installation:** -```bash -# Add to PATH (see instructions from install output) -# Then use from anywhere -jan-cli --version -jan-cli config validate -jan-cli service list -``` - -### Method 2: Wrapper Scripts (No Installation) - -Run directly from project root using wrapper scripts: - -```bash -# Linux/macOS -./jan-cli.sh config validate -./jan-cli.sh service list - -# Windows PowerShell -.\jan-cli.ps1 config validate -.\jan-cli.ps1 service list -``` - -**Advantages:** -- No installation needed -- Auto-builds if binary missing or outdated -- Always uses latest code -- Good for development - -**Disadvantages:** -- Must be run from project root -- Requires file extension (.sh or.ps1) - -### Method 3: Manual Build - -```bash -# Navigate to CLI directory -cd cmd/jan-cli - -# Build -go build - -# Run -./jan-cli --help # Linux/macOS -.\jan-cli.exe --help # Windows - -# Optional: Copy to a location in your PATH -cp jan-cli ~/bin/ # Linux/macOS -copy jan-cli.exe %USERPROFILE%\bin\ # Windows -``` - -### Makefile Targets - -```bash -make cli-build # Build the binary -make cli-install # Build and install to local bin -make cli-clean # Remove the binary -``` - -**cli-build** - Builds binary in `tools/jan-cli/`: -- Linux/macOS: `tools/jan-cli/jan-cli` -- Windows: `tools/jan-cli/jan-cli.exe` - -**cli-install** - Builds and installs: -1. Calls `cli-build` -2. Creates bin directory if needed -3. Copies binary -4. Shows PATH instructions - -**cli-clean** - Removes binary: -- Useful for clean rebuilds -- Frees disk space - ---- - -## Commands Reference - -### Command Hierarchy - -``` -jan-cli (root) -+-- config (configuration management) -| +-- validate - Validate configuration files -| +-- export - Export configuration -| +-- show - Display configuration values -| +-- generate - Generate schemas and defaults -+-- service (service operations) -| +-- list - List all services -| +-- logs - Show service logs -| +-- status - Check service status -+-- dev (development tools) -| +-- setup - Initialize development environment -| +-- scaffold - Generate new service from template -+-- swagger (API documentation) -| +-- generate - Generate OpenAPI documentation -+-- completion (shell completions) - +-- bash - +-- zsh - +-- fish - +-- powershell -``` - -### Global Flags - -Available on all commands: - -- `-v, --verbose` - Enable verbose output -- `--config-dir ` - Configuration directory (default: "config") -- `-h, --help` - Show help -- `--version` - Show version - ---- - -## Configuration Management - -The `config` subcommand manages Jan Server configuration files. - -### config validate - -Validate configuration files against schema: - -```bash -# Validate with default environment -jan-cli config validate - -# Validate specific environment -jan-cli config validate --env production -jan-cli config validate --env development - -# Verbose validation -jan-cli config validate --verbose -``` - -**Output:** -- OK Configuration valid -- [X] Validation errors with details - -### config export - -Export configuration in various formats: - -```bash -# Export as environment variables -jan-cli config export --format env - -# Export as Docker env file -jan-cli config export --format docker-env --output.env - -# Export as JSON -jan-cli config export --format json --output config.json - -# Export as YAML -jan-cli config export --format yaml --output config.yaml - -# Export for specific environment -jan-cli config export --env production --format env -``` - -**Formats:** -- `env` - Shell environment variables (`KEY=value`) -- `docker-env` - Docker Compose env file -- `json` - JSON format -- `yaml` - YAML format - -**Flags:** -- `--format ` - Output format (required) -- `--output ` - Output file (default: stdout) -- `--env ` - Environment to export - -### config show - -Display configuration values with path navigation: - -```bash -# Show all configuration -jan-cli config show - -# Show specific service -jan-cli config show llm-api -jan-cli config show media-api - -# Show as JSON -jan-cli config show llm-api --format json - -# Show with specific environment -jan-cli config show llm-api --env production -``` - -**Flags:** -- `` - Service name (optional) -- `--format ` - Output format (yaml, json) -- `--env ` - Environment - -### config generate - -Generate JSON schemas and defaults.yaml: - -```bash -# Generate all schemas -jan-cli config generate - -# Generates: -# - config/schema/config.schema.json -# - config/schema/inference.schema.json -# - config/schema/infrastructure.schema.json -# - config/schema/monitoring.schema.json -# - config/schema/services.schema.json -# - config/defaults.yaml -``` - ---- - -## Service Operations - -The `service` subcommand manages Jan Server services. - -### service list - -List all available services: - -```bash -jan-cli service list -``` - -**Output:** -``` -Available services: - llm-api:8080 LLM API - OpenAI-compatible chat completions - media-api:8285 Media API - File upload and management - response-api:8082 Response API - Multi-step orchestration - mcp-tools:8091 MCP Tools - Model Context Protocol tools -``` - -### service logs - -Show Docker Compose logs for a specific service: - -```bash -# View logs for a service -jan-cli service logs llm-api - -# Follow logs -jan-cli service logs llm-api --follow - -# Show last N lines -jan-cli service logs llm-api --tail 50 -``` - -`jan-cli service logs` wraps `docker compose logs`, so it works on every platform where Docker Desktop is installed. - -### service status - -Check container status (and optionally health endpoints): - -```bash -# Check all services via Makefile health check -jan-cli service status - -# Check specific service -jan-cli service status llm-api -``` - -- `jan-cli service status` without arguments runs `make health-check` -- With a service argument it shows `docker compose ps ` and invokes the service-specific `/healthz` endpoint (PowerShell `Invoke-WebRequest` on Windows or `curl` on macOS/Linux) - ---- - -## Development Tools - -The `dev` subcommand provides development utilities. - -### dev setup - -Initialize development environment: - -```bash -jan-cli dev setup -``` - -**What it does:** -1. Creates required directories (logs/, tmp/, uploads/) -2. Creates Docker networks (jan-network, jan-dev) -3. Generates.env file from templates -4. Optional: Sets up Docker environment - -**Features:** -- OK Cross-platform (Windows, Linux, macOS) -- OK Docker optional (warns if not available) -- OK Idempotent (safe to run multiple times) - -### dev scaffold - -Generate a new service from `services/template-api`: - -```bash -# Create new API service -jan-cli dev scaffold my-service - -# Specify template/port (future templates can be added later) -jan-cli dev scaffold worker-service --template api --port 8999 -``` - -What it does today: -- Copies `services/template-api` to `services/` -- Replaces placeholders (module import paths, README text, comments) -- Prints next steps (run `go mod tidy`, update docker-compose, add Kong routes) - -If the destination already exists the command aborts without touching files. - ---- - -## Swagger Documentation - -The `swagger` subcommand generates OpenAPI documentation. - -### swagger generate - -Generate OpenAPI/Swagger documentation for services: - -```bash -# Generate for specific service -jan-cli swagger generate --service llm-api -jan-cli swagger generate --service media-api - -# Generates: -# - services/llm-api/docs/swagger.yaml -# - services/llm-api/docs/swagger.json -``` - -**Requirements:** -- Service must have Swagger annotations in code -- `swag` CLI tool must be installed (`go install github.com/swaggo/swag/cmd/swag@latest`) - ---- - -## Troubleshooting - -### "jan-cli: command not found" (Linux/macOS) - -**Problem:** The bin directory is not in your PATH. - -**Solution:** -1. Check if `~/bin` exists: - ```bash - ls ~/bin/jan-cli - ``` - -2. Add to PATH: - ```bash - export PATH="$PATH:$HOME/bin" - ``` - -3. Make permanent by adding to `~/.bashrc` or `~/.zshrc`: - ```bash - echo 'export PATH="$PATH:$HOME/bin"' >> ~/.bashrc - source ~/.bashrc - ``` - -### "jan-cli is not recognized" (Windows) - -**Problem:** The bin directory is not in your PATH. - -**Solution:** -1. Check if file exists: - ```powershell - Test-Path $env:USERPROFILE\bin\jan-cli.exe - ``` - -2. Add to PATH (temporary): - ```powershell - $env:PATH += ";$env:USERPROFILE\bin" - ``` - -3. Make permanent: - ```powershell - notepad $PROFILE - # Add this line: - $env:PATH += ";$env:USERPROFILE\bin" - ``` - -4. Restart PowerShell - -### "Permission denied" (Linux/macOS) - -**Problem:** The binary is not executable. - -**Solution:** -```bash -chmod +x ~/bin/jan-cli -``` - -The `make cli-install` target handles this automatically, but if you installed manually, you may need to set execute permissions. - -### Binary Not Updated After Code Changes - -**Problem:** Installed binary is outdated after modifying source code. - -**Solution:** -```bash -# Rebuild and reinstall -make cli-install - -# Or clean and rebuild -make cli-clean -make cli-install -``` - -### Wrapper Scripts Don't Work - -**Problem:** Wrapper script shows errors or doesn't build. - -**Solution:** -1. Ensure Go is installed: - ```bash - go version - ``` - -2. Ensure in project root: - ```bash - pwd # Should show jan-server directory - ``` - -3. Check script is executable (Linux/macOS): - ```bash - chmod +x jan-cli.sh - ``` - -4. Try manual build: - ```bash - cd tools/jan-cli && go build - ---- - -## Shell Completion - -Jan CLI supports shell completion for bash, zsh, fish, and PowerShell. - -### Generate Completion Script - -```bash -# Bash -jan-cli completion bash > /etc/bash_completion.d/jan-cli - -# Zsh -jan-cli completion zsh > "${fpath[1]}/_jan-cli" - -# Fish -jan-cli completion fish > ~/.config/fish/completions/jan-cli.fish - -# PowerShell -jan-cli completion powershell > jan-cli.ps1 -# Then source it in your profile -``` - -### Enable Completion - -**Bash:** -```bash -# Add to ~/.bashrc -source /etc/bash_completion.d/jan-cli -``` - -**Zsh:** -```zsh -# Add to ~/.zshrc -autoload -U compinit -compinit -``` - -**Fish:** -```fish -# Completion is auto-loaded from ~/.config/fish/completions/ -``` - -**PowerShell:** -```powershell -# Add to $PROFILE -. /path/to/jan-cli.ps1 -``` - ---- - -## Technical Details - -### Framework: Cobra - -Jan CLI uses [spf13/cobra](https://github.com/spf13/cobra) v1.8.1, the industry-standard CLI framework. - -**Why Cobra:** -- Used by kubectl, docker, gh, helm -- Auto-generated help text -- Built-in completion generation -- Nested subcommand support -- Flag parsing and validation -- POSIX-compliant - -**Dependencies:** -```go -require ( - github.com/spf13/cobra v1.8.1 - gopkg.in/yaml.v3 v3.0.1 -) -``` - -### Project Structure - -``` -tools/jan-cli/ -+-- main.go # Root command and initialization -+-- cmd_config.go # Configuration management -+-- cmd_service.go # Service operations -+-- cmd_dev.go # Development tools -+-- cmd_setup.go # Interactive setup wizard -+-- cmd_swagger.go # Swagger generation -+-- utils.go # Utility functions -+-- go.mod # Go module dependencies -+-- README.md # CLI documentation -``` - -### Build Details - -**Build Command:** -```bash -cd tools/jan-cli -go build -o jan-cli -``` - -**Cross-Platform Builds:** -```bash -# Linux -GOOS=linux GOARCH=amd64 go build -o jan-cli-linux - -# macOS -GOOS=darwin GOARCH=amd64 go build -o jan-cli-darwin - -# Windows -GOOS=windows GOARCH=amd64 go build -o jan-cli.exe -``` - -**Binary Size:** ~10MB (includes dependencies) - -### Wrapper Scripts - -**PowerShell (jan-cli.ps1):** -- Auto-builds if binary missing or outdated -- Checks all `*.go` files for changes -- Supports all jan-cli commands -- Works on Windows PowerShell 5.1+ - -**Bash (jan-cli.sh):** -- Auto-builds if binary missing or outdated -- Checks all `*.go` files for changes -- Supports all jan-cli commands -- Works on Linux/macOS with Bash 3.2+ - ---- - -## Examples - -### Configuration Workflow - -```bash -# Generate schemas and defaults -jan-cli config generate - -# Validate configuration -jan-cli config validate --env production - -# Export as environment variables -jan-cli config export --format env --env production >.env.production - -# Show specific service config -jan-cli config show llm-api --format json -``` - -### Service Management - -```bash -# List all services -jan-cli service list - -# View logs -jan-cli service logs llm-api --follow - -# Check health -jan-cli service status -``` - -### Development Setup - -```bash -# Setup environment -jan-cli dev setup - -# Create new service from template -jan-cli dev scaffold worker-service --template api - -# Generate API documentation -jan-cli swagger generate --service llm-api -``` - ---- - -## Best Practices - -### For Daily Use - -1. Install globally with `make cli-install` -2. Add to PATH once -3. Use `jan-cli` from anywhere -4. Run `make cli-install` after pulling updates - -### For Development - -1. Use wrapper scripts (`./jan-cli.sh` or `.\jan-cli.ps1`) -2. Always uses latest code -3. Auto-builds if needed -4. Good for testing changes - -### For CI/CD - -1. Use wrapper scripts (no installation needed) -2. Or install and add to PATH -3. Verify with `jan-cli --version` -4. Run commands directly - ---- - -## Summary - -**Quick Reference:** -- **Build:** `make cli-build` -- **Install:** `make cli-install` -- **Clean:** `make cli-clean` -- **Use:** `jan-cli ` - -**Recommended Workflow:** -1. Run `make cli-install` once -2. Add to PATH as instructed -3. Use `jan-cli` from anywhere -4. Run `make cli-install` again after updates - -**Key Commands:** -- `jan-cli config validate` - Validate configuration -- `jan-cli config generate` - Generate schemas -- `jan-cli service list` - List services -- `jan-cli dev setup` - Setup environment -- `jan-cli swagger generate --service ` - Generate API docs - ---- - -## Related Documentation - -- [Testing Guide](./testing) - Cross-platform testing procedures -- [Configuration System](../configuration/) - Configuration management -- [Development Guide](./development) - Local development setup -- [Architecture Overview](../architecture/) - System design - ---- - -**Status:** Production Ready OK -**Version:** 1.0.0 -**Cross-Platform:** Windows, Linux, macOS diff --git a/apps/platform/content/docs/guides/kong-plugins.mdx b/apps/platform/content/docs/guides/kong-plugins.mdx deleted file mode 100644 index ef286985..00000000 --- a/apps/platform/content/docs/guides/kong-plugins.mdx +++ /dev/null @@ -1,279 +0,0 @@ ---- -title: "Kong Custom Plugin Setup Guide" ---- - -# Kong Custom Plugin Setup Guide - -This guide explains how to set up and use custom Kong plugins in the jan-server project. - -## Directory Structure - -``` -kong/ -+-- kong.yml # Main Kong declarative config -+-- kong-dev-full.yml # Dev-Full/Hybrid mode config (host routing) -+-- plugins/ # Custom plugins directory - +-- keycloak-apikey/ # API key validation plugin - +-- handler.lua # Plugin logic - +-- schema.lua # Configuration schema - +-- README.md # Plugin documentation -``` - -## Plugin Loading - -### Docker Configuration - -Kong is configured to load custom plugins via environment variables: - -```yaml -environment: - KONG_PLUGINS: bundled,keycloak-apikey # Load bundled + custom plugins - KONG_LUA_PACKAGE_PATH: /usr/local/kong/plugins/?.lua;; # Plugin search path - -volumes: - -../kong/plugins:/usr/local/kong/plugins:ro # Mount plugins directory -``` - -### Verification - -After starting Kong, verify plugins are loaded: - -```bash -# List all enabled plugins -curl http://localhost:8001/plugins/enabled - -# Should include: -# - bundled plugins (jwt, rate-limiting, cors, etc.) -# - keycloak-apikey (custom) -``` - -## Creating New Plugins - -### 1. Create Plugin Directory - -```bash -mkdir -p kong/plugins/my-plugin -``` - -### 2. Create handler.lua - -```lua -local MyPluginHandler = { - PRIORITY = 1000, -- Plugin execution priority - VERSION = "1.0.0", -} - -function MyPluginHandler:access(conf) - -- Your plugin logic here - kong.log.info("My plugin executed!") -end - -return MyPluginHandler -``` - -### 3. Create schema.lua - -```lua -return { - name = "my-plugin", - fields = { - { config = { - type = "record", - fields = { - { my_setting = { - type = "string", - required = true, - default = "default_value", - }}, - } - }}, - }, -} -``` - -### 4. Register Plugin - -Update `docker/infrastructure.yml`: - -```yaml -environment: - KONG_PLUGINS: bundled,keycloak-apikey,my-plugin # Add your plugin -``` - -### 5. Use in kong.yml - -```yaml -plugins: - - name: my-plugin - tags: [custom] - config: - my_setting: "value" -``` - -## Plugin Development Tips - -### Debugging - -1. **Enable debug logging:** -```yaml -environment: - KONG_LOG_LEVEL: debug -``` - -2. **Watch logs in real-time:** -```bash -docker logs kong -f -``` - -3. **Add debug statements:** -```lua -kong.log.debug("Variable value: ", some_variable) -kong.log.err("Error occurred: ", error_message) -``` - -### Testing Locally - -1. **Reload Kong after changes:** -```bash -docker restart kong -``` - -2. **Test plugin behavior:** -```bash -# Make test request -curl -v http://localhost:8000/your-endpoint \ - -H "X-Custom-Header: value" - -# Check response headers -curl -I http://localhost:8000/your-endpoint -``` - -### Plugin Priority - -Kong executes plugins in priority order (higher = earlier): - -``` -2000+ - Pre-processing (e.g., request transformation) -1000+ - Authentication (e.g., jwt: 1005, keycloak-apikey: 1002) -500+ - Authorization -100+ - Post-processing -``` - -Set priority in `handler.lua`: -```lua -local MyPluginHandler = { - PRIORITY = 1002, -- Your priority -} -``` - -## Common Patterns - -### HTTP Requests - -```lua -local http = require "resty.http" -local httpc = http.new() - -local res, err = httpc:request_uri("http://service:8080/endpoint", { - method = "POST", - body = "data", - headers = { - ["Content-Type"] = "application/json", - }, -}) - -if res.status == 200 then - kong.log.info("Request successful") -end -``` - -### Header Manipulation - -```lua --- Read headers -local api_key = kong.request.get_header("X-API-Key") - --- Set request headers (to upstream) -kong.service.request.set_header("X-User-ID", "123") - --- Set response headers (to client) -kong.response.set_header("X-Custom", "value") - --- Remove headers -kong.service.request.clear_header("Authorization") -``` - -### Authentication - -```lua --- Authenticate consumer for rate limiting -kong.client.authenticate({ - id = user_id, - custom_id = user_subject, -}) -``` - -### Error Responses - -```lua --- Return error to client -return kong.response.exit(401, { - message = "Unauthorized" -}) -``` - -## Best Practices - -1. **Error Handling**: Always handle HTTP errors gracefully -2. **Logging**: Use appropriate log levels (debug, info, warn, err) -3. **Performance**: Cache expensive operations, reuse HTTP connections -4. **Security**: Validate all inputs, sanitize data -5. **Configuration**: Use schema.lua for type-safe config -6. **Testing**: Test with both valid and invalid inputs - -## Resources - -- [Kong Plugin Development Guide](https://docs.konghq.com/gateway/latest/plugin-development/) -- [Kong PDK Reference](https://docs.konghq.com/gateway/latest/plugin-development/pdk/) -- [Lua Reference](https://www.lua.org/manual/5.1/) - -## Troubleshooting - -### Plugin Not Loaded - -**Symptom**: Plugin not in `/plugins/enabled` - -**Solutions**: -1. Check `KONG_PLUGINS` includes your plugin name -2. Verify plugin files are mounted correctly -3. Check file permissions (must be readable) -4. Restart Kong container - -### Syntax Errors - -**Symptom**: Kong fails to start - -**Solutions**: -1. Check Kong logs: `docker logs kong` -2. Validate Lua syntax: `luac -p handler.lua` -3. Check schema format matches Kong requirements - -### Plugin Not Executing - -**Symptom**: Plugin loaded but not running - -**Solutions**: -1. Verify plugin is configured in `kong.yml` -2. Check route/service matches request -3. Ensure priority doesn't conflict with other plugins -4. Add debug logging to verify execution - -### Performance Issues - -**Symptom**: Slow response times - -**Solutions**: -1. Profile plugin execution time -2. Add caching for expensive operations -3. Use connection pooling for HTTP requests -4. Consider async operations if possible diff --git a/apps/platform/content/docs/guides/mcp-admin-interface.mdx b/apps/platform/content/docs/guides/mcp-admin-interface.mdx deleted file mode 100644 index 8f53ccb1..00000000 --- a/apps/platform/content/docs/guides/mcp-admin-interface.mdx +++ /dev/null @@ -1,375 +0,0 @@ ---- -title: "MCP Admin Interface Guide" ---- - -# MCP Admin Interface Guide - -Complete guide for administrators managing MCP (Model Context Protocol) tools without code changes. - -## Overview - -The MCP Admin Interface allows administrators to dynamically configure MCP tools through the LLM API, without requiring service restarts or code deployments. This guide covers: - -- Viewing and listing MCP tools -- Editing tool configurations -- Enabling/disabling tools -- Setting content filtering rules -- Managing tool access and permissions - -## Architecture - -``` -Platform Web (Admin UI) - ↓ -LLM API - ↓ -Database (admin_mcp_tools table) - ↓ -MCP Tools Service (Tool Registration) -``` - -## Quick Start - -### List Available MCP Tools - -**GET** `/v1/admin/mcp/tools` - -View all MCP tools available in your system. - -```bash -curl -H "Authorization: Bearer " \ - http://localhost:8000/v1/admin/mcp/tools -``` - -Response: -```json -{ - "data": [ - { - "id": "tool_1", - "tool_key": "google_search", - "name": "Google Search", - "description": "Search the web using Google", - "category": "search", - "enabled": true, - "disallowed_keywords": [] - }, - { - "id": "tool_2", - "tool_key": "python_exec", - "name": "Python Executor", - "description": "Execute Python code securely", - "category": "code", - "enabled": true, - "disallowed_keywords": ["delete", "drop", "rm"] - } - ] -} -``` - -### Enable/Disable a Tool - -**PATCH** `/v1/admin/mcp/tools/{tool_id}` - -Enable or disable a tool for all users. - -```bash -curl -X PATCH http://localhost:8000/v1/admin/mcp/tools/tool_1 \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "enabled": false - }' -``` - -## Managing MCP Tools - -### Get Tool Details - -**GET** `/v1/admin/mcp/tools/{tool_id}` - -View complete configuration for a specific tool. - -```bash -curl -H "Authorization: Bearer " \ - http://localhost:8000/v1/admin/mcp/tools/tool_2 -``` - -Response: -```json -{ - "id": "tool_2", - "tool_key": "python_exec", - "name": "Python Executor", - "description": "Execute Python code", - "category": "code", - "enabled": true, - "disallowed_keywords": [ - "delete", - "drop", - "format", - "rm.*recursive" - ], - "created_at": "2025-12-20T10:00:00Z", - "updated_at": "2025-12-23T11:00:00Z" -} -``` - -### Update Tool Configuration - -**PATCH** `/v1/admin/mcp/tools/{tool_id}` - -Modify tool name, description, or enable/disable status. - -```bash -curl -X PATCH http://localhost:8000/v1/admin/mcp/tools/tool_2 \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "name": "Safe Python Executor", - "description": "Execute sandboxed Python code", - "enabled": true - }' -``` - -### Set Content Filtering Rules - -**PUT** `/v1/admin/mcp/tools/{tool_id}/filters` - -Configure regex patterns to block certain keywords/patterns in tool arguments. - -```bash -curl -X PUT http://localhost:8000/v1/admin/mcp/tools/tool_2/filters \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "disallowed_keywords": [ - "^delete\\s+", - "^drop\\s+", - "^rm\\s+-rf", - "os\\.remove", - "__import__\\(.*os.*\\)" - ] - }' -``` - -## Content Filtering - -### Filter Syntax - -Disallowed keywords use **regex patterns** for flexible matching: - -| Pattern | Matches | Use Case | -|---------|---------|----------| -| `^delete\\s+` | "delete" at start of string | Block SQL DELETE commands | -| `drop` | "drop" anywhere (case-insensitive) | Block DROP statements | -| `os\\.remove` | "os.remove" in Python code | Block file deletion | -| `__import__` | Python import statements | Restrict imports | -| `eval\\(` | "eval(" function calls | Block dangerous eval | -| `exec\\(` | "exec(" function calls | Block exec calls | - -### Common Filter Sets - -#### Python Code Execution - Safe Mode - -Block potentially dangerous operations: - -```json -{ - "disallowed_keywords": [ - "^import os", - "^import subprocess", - "^import socket", - "^import requests", - "os\\.system", - "os\\.remove", - "subprocess\\.call", - "subprocess\\.run", - "open\\(", - "__import__" - ] -} -``` - -#### Web Search - Content Filter - -Block adult/NSFW searches: - -```json -{ - "disallowed_keywords": [ - "adult", - "nsfw", - "porn", - "xxx", - "sex.*site", - "18\\+.*content" - ] -} -``` - -#### General Safety Rules - -```json -{ - "disallowed_keywords": [ - "delete", - "drop", - "truncate", - "destroy", - "rm\\s+-rf", - "format.*drive" - ] -} -``` - -## Python Examples - -## JavaScript Examples - -### Bulk Tool Status Update - -```javascript -const adminToken = 'your_admin_token'; -const headers = { - 'Authorization': `Bearer ${adminToken}`, - 'Content-Type': 'application/json' -}; - -// Fetch all tools -const listResponse = await fetch('http://localhost:8000/v1/admin/mcp/tools', { - headers -}); - -const tools = await listResponse.json(); - -// Disable all code execution tools -for (const tool of tools.data) { - if (tool.category === 'code') { - await fetch(`http://localhost:8000/v1/admin/mcp/tools/${tool.id}`, { - method: 'PATCH', - headers, - body: JSON.stringify({ enabled: false }) - }); - console.log(`Disabled: ${tool.name}`); - } -} -``` - -## Access Control - -### Admin Authentication - -The MCP Admin endpoints require admin-level authentication. Ensure your token has appropriate permissions: - -```bash -# Admin user login -curl -X POST http://localhost:8000/v1/auth/login \ - -H "Content-Type: application/json" \ - -d '{ - "email": "admin@example.com", - "password": "secure_password" - }' - -# Use the returned token for admin operations -``` - -### Role-Based Access Control (RBAC) - -MCP Admin endpoints check for: -- `admin` role OR -- `mcp:admin` permission - -Configure in your identity provider (Keycloak): - -1. Create `mcp_admin` group in Keycloak -2. Add admin users to the group -3. Map group to `mcp:admin` scope in JWT token -4. Token will include `"scope": "mcp:admin"` - -## Troubleshooting - -### "Permission Denied" Error - -**Problem**: Receiving 403 Forbidden when accessing admin endpoints - -**Solutions**: -1. Verify token has admin permissions -2. Check token hasn't expired: `jwt decode YOUR_TOKEN` -3. Ensure user is in admin group/role in Keycloak - -### Filter Not Applied - -**Problem**: Content filter isn't blocking expected keywords - -**Solutions**: -1. Verify regex syntax is correct -2. Test regex pattern independently -3. Remember regexes are case-sensitive (use `(?i)` for case-insensitive) -4. Check filter is applied to correct tool - -### Tool Not Appearing - -**Problem**: Tool exists but doesn't show in admin list - -**Solutions**: -1. Verify MCP Tools service is running -2. Check service has registered the tool -3. Ensure tool registration completed (may need restart) - -## Best Practices - -### 1. Enable Only Needed Tools - -Start with minimal tool set, enable others as needed: - -```bash -# Disable all tools initially -curl -X PATCH http://localhost:8000/v1/admin/mcp/tools/web_scraper \ - -H "Authorization: Bearer " \ - -d '{"enabled": false}' -``` - -### 2. Test Filters Before Deployment - -Test filter patterns against actual use cases: - -```bash -# Pattern to test: "os\\.system" -# Test inputs: -# - "os.system('ls')" → BLOCKED ✓ -# - "my_os.system_call()" → ALLOWED ✓ -``` - -### 3. Audit Tool Usage - -Monitor which tools users are accessing: - -```bash -# Check MCP Tools service logs -docker logs mcp-tools | grep "tool_call" -``` - -### 4. Regular Security Updates - -Review tool filters quarterly as new security threats emerge. - -## Related Documentation - -- [MCP Tools API Reference](../api/mcp-tools/) - Tool execution API -- [Authentication Guide](./authentication) - Admin authentication -- [Security Guide](../architecture/security) - Security best practices -- [Deployment Guide](./deployment) - Production configuration - -## Support - -For admin-related issues: -- Check [Troubleshooting Guide](./troubleshooting) -- Review [MCP Tools Documentation](../api/mcp-tools/) -- Contact your Jan Server administrator - ---- - -**Last Updated**: December 23, 2025 -**Compatibility**: Jan Server v0.0.14+ -**Role Required**: Admin or `mcp:admin` permission diff --git a/apps/platform/content/docs/guides/mcp-testing.mdx b/apps/platform/content/docs/guides/mcp-testing.mdx deleted file mode 100644 index 57880df3..00000000 --- a/apps/platform/content/docs/guides/mcp-testing.mdx +++ /dev/null @@ -1,128 +0,0 @@ ---- -title: "MCP Testing Guide" ---- - -# MCP Testing Guide - -Validate the MCP (Model Context Protocol) toolchain end to end. Every command below maps directly to current Makefile targets and compose services, so you can run it without editing scripts. - -## 1. Prerequisites - -- `make up-full` (or `make up-mcp` + `make up-api`) so Kong, MCP Tools, and vector-store are running -- `SERPER_API_KEY` and other MCP-related env vars set in `.env` -- Services reachable on: - - Kong Gateway: http://localhost:8000 - - MCP Tools: http://localhost:8091 (direct) or http://localhost:8000/mcp (via Kong) - - Vector Store: http://localhost:3015 - - SandboxFusion (optional): http://localhost:3010 - -Check health quickly: -```bash -make health-check # full stack health summary -curl http://localhost:8091/healthz -curl http://localhost:3015/healthz || true # returns 404 because the vector store uses custom routes -``` - -## 2. Automated Suite (jan-cli api-test) - -Run everything through the Makefile target: - -```bash -make test-mcp-integration -``` - -The target executes: -```bash -jan-cli api-test run tests/automation/mcp-postman-scripts.json \ - --env-var "kong_url=http://localhost:8000" \ - --env-var "mcp_tools_url=http://localhost:8000/mcp" \ - --verbose --reporters cli -``` - -Expectations: -- Guest token requests succeed (`/llm/auth/guest-login`) -- MCP search variants (domain filter, offline) return structured JSON -- Tool list includes `google_search`, `scrape`, `file_search_index`, `file_search_query`, `python_exec` -- File index/query flows return 200 and include the previously indexed document -- SandboxFusion executions return stdout/stderr - -## 3. Manual Checks - -### 3.1 Kong -> MCP Tools - -```bash -# list tools through Kong (authenticated by the gateway) -curl -s http://localhost:8000/mcp -X POST -H "Content-Type: application/json" -d '{ - "jsonrpc": "2.0", - "id": 1, - "method": "tools/list" -}' | jq . - -# call python_exec via Kong -curl -s http://localhost:8000/mcp -X POST -H "Content-Type: application/json" -d '{ - "jsonrpc": "2.0", - "id": 2, - "method": "tools/call", - "params": { - "name": "python_exec", - "arguments": { - "code": "print(\"Hello from MCP\")", - "language": "python", - "approved": true - } - } -}' | jq . -``` - -### 3.2 Direct Service Endpoints - -```bash -# MCP Tools (direct port) -curl -s http://localhost:8091/v1/mcp -H "Content-Type: application/json" -d '{ - "jsonrpc": "2.0", - "id": 3, - "method": "tools/call", - "params": {"name": "file_search_index", "arguments": {"document_id": "cli-doc", "text": "CLI test"}} -}' | jq . - -# Vector store -curl -s http://localhost:3015/documents -X POST -H "Content-Type: application/json" -d '{ - "document_id": "curl-doc", - "text": "Curl-based MCP test", - "metadata": {"owner": "qa"} -}' - -curl -s http://localhost:3015/query -X POST -H "Content-Type: application/json" -d '{ - "text": "MCP test", - "top_k": 3 -}' | jq . - -# SandboxFusion (optional) -curl -s http://localhost:3010/run_code -H "Content-Type: application/json" -d '{ - "code": "print(\"sandbox\")", - "language": "python" -}' | jq . -``` - -## 4. Logs and Troubleshooting - -| Component | Logs | Notes | -|-----------|------|-------| -| Kong | `make logs` or `docker compose logs kong` | Confirms `/mcp` route, auth headers, upstream failures | -| MCP Tools | `make logs-mcp` | Watch tool dispatch, vector store responses, sandbox output | -| Vector Store | `docker compose logs vector-store` | Service name is `vector-store` in `docker/services-mcp.yml` | -| SandboxFusion | `docker compose logs sandboxfusion` (if enabled) | Verify HTTP 200s and stdout capturing | - -Common fixes: -- **401/403**: ensure guest token exists or provide API key headers when hitting Kong -- **Timeouts to vector store**: confirm service is part of the `mcp` profile (`COMPOSE_PROFILES` includes `mcp`) -- **Sandbox errors**: include the required `language` parameter; see `services/mcp-tools/internal/sandboxfusion` - -## 5. Summary Checklist - -- [ ] `make up-full` (or `make up-mcp` + `make up-api`) running -- [ ] `make test-mcp-integration` passes locally -- [ ] Manual curl checks through Kong and direct service succeed -- [ ] Logs show healthy MCP tool executions - -Document these results in your PR or QA notes so MCP coverage stays verifiable. diff --git a/apps/platform/content/docs/guides/meta.json b/apps/platform/content/docs/guides/meta.json deleted file mode 100644 index ef9e1697..00000000 --- a/apps/platform/content/docs/guides/meta.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "title": "Guides", - "pages": [ - "index", - "development", - "deployment", - "authentication", - "monitoring", - "monitoring-advanced", - "testing", - "troubleshooting", - "jan-cli", - "kong-plugins", - "mcp-testing", - "mcp-admin-interface", - "conversation-management", - "prompt-orchestration", - "background-mode", - "webhooks", - "services-template", - "user-settings-personalization", - "user-management-todo" - ] -} diff --git a/apps/platform/content/docs/guides/monitoring-advanced.mdx b/apps/platform/content/docs/guides/monitoring-advanced.mdx deleted file mode 100644 index 84c5c31a..00000000 --- a/apps/platform/content/docs/guides/monitoring-advanced.mdx +++ /dev/null @@ -1,529 +0,0 @@ ---- -title: "Monitoring & Troubleshooting Deep Dive" ---- - -# Monitoring & Troubleshooting Deep Dive - -> **Status:** v0.0.14 | **Last Updated:** December 23, 2025 - -Production monitoring is critical for maintaining Jan Server reliability. This guide covers health checks, metrics collection, distributed tracing, troubleshooting common issues, and performance optimization. - -## Table of Contents - -- [Service Health Monitoring](#service-health-monitoring) -- [Metrics & Observability](#metrics--observability) -- [Distributed Tracing](#distributed-tracing) -- [Common Issues & Solutions](#common-issues--solutions) -- [Performance Optimization](#performance-optimization) -- [Logging Strategies](#logging-strategies) -- [Capacity Planning](#capacity-planning) -- [Incident Response](#incident-response) - ---- - -## Service Health Monitoring - -### Health Check Endpoints - -All services expose health check endpoints: - -```bash -# LLM API health -curl http://localhost:8080/health - -# Response API health -curl http://localhost:8082/health - -# Media API health -curl http://localhost:8285/health - -# MCP Tools health -curl http://localhost:8091/health - -# Template API health -curl http://localhost:8185/health -``` - -**Response Format:** - -```json -{ - "status": "ok", - "version": "v0.0.14", - "timestamp": "2025-12-23T12:00:00Z", - "uptime_seconds": 3600, - "database": { - "connected": true, - "latency_ms": 2 - }, - "dependencies": { - "redis": { - "connected": true, - "latency_ms": 1 - }, - "message_queue": { - "connected": true, - "queue_depth": 5 - } - } -} -``` - -### Kubernetes Probe Configuration - -```yaml -apiVersion: v1 -kind: Pod -metadata: - name: jan-server-llm-api -spec: - containers: - - name: llm-api - image: jan-server:v0.0.14 - ports: - - containerPort: 8080 - - # Readiness probe - Accept traffic? - readinessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 5 - periodSeconds: 10 - timeoutSeconds: 3 - failureThreshold: 3 - - # Liveness probe - Container alive? - livenessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 30 - periodSeconds: 30 - timeoutSeconds: 5 - failureThreshold: 3 - - # Startup probe - App started? - startupProbe: - httpGet: - path: /health - port: 8080 - failureThreshold: 30 - periodSeconds: 10 -``` - -### Custom Health Checks - ---- - -## Metrics & Observability - -### Key Metrics by Service - -**LLM API Metrics:** - -``` -llm_api_requests_total # Total API requests -llm_api_request_duration_seconds # Request latency histogram -llm_api_conversations_total # Total conversations created -llm_api_messages_total # Total messages sent -llm_api_tokens_processed_total # Tokens processed -llm_api_errors_total # Error count by type -llm_api_cache_hits_total # Cache hit rate -llm_api_active_conversations # Concurrent conversations -``` - -**Response API Metrics:** - -``` -response_api_generations_total # Response generations -response_api_generation_duration_seconds # Generation latency -response_api_tokens_generated_total # Tokens in responses -response_api_errors_total # Generation errors -``` - -**Media API Metrics:** - -``` -media_api_uploads_total # File uploads -media_api_upload_size_bytes # Upload size distribution -media_api_upload_duration_seconds # Upload latency -media_api_storage_bytes_used # Total storage used -media_api_errors_total # Upload errors -``` - -### Prometheus Configuration - -```yaml -# prometheus.yml -global: - scrape_interval: 15s - evaluation_interval: 15s - -alerting: - alertmanagers: - - static_configs: - - targets: ['localhost:9093'] - -rule_files: - - 'alert_rules.yml' - -scrape_configs: - - job_name: 'jan-llm-api' - metrics_path: '/metrics' - static_configs: - - targets: ['localhost:8080'] - - - job_name: 'jan-response-api' - metrics_path: '/metrics' - static_configs: - - targets: ['localhost:8082'] - - - job_name: 'jan-media-api' - metrics_path: '/metrics' - static_configs: - - targets: ['localhost:8285'] - - - job_name: 'jan-mcp-tools' - metrics_path: '/metrics' - static_configs: - - targets: ['localhost:8091'] - - - job_name: 'postgres' - static_configs: - - targets: ['localhost:9187'] - - - job_name: 'redis' - static_configs: - - targets: ['localhost:9121'] -``` - -### Alert Rules - -```yaml -# alert_rules.yml -groups: -- name: jan_server_alerts - interval: 30s - rules: - - # Service down - - alert: ServiceDown - expr: up{job=~"jan-.*"} == 0 - for: 2m - annotations: - summary: "{{ $labels.job }} is down" - - # High error rate - - alert: HighErrorRate - expr: | - rate(llm_api_errors_total[5m]) > 0.05 - for: 5m - annotations: - summary: "High error rate in LLM API" - description: "Error rate is {{ $value }}" - - # High latency - - alert: HighLatency - expr: | - histogram_quantile(0.99, rate(llm_api_request_duration_seconds_bucket[5m])) > 5 - for: 5m - annotations: - summary: "High request latency detected" - description: "P99 latency is {{ $value }}s" - - # Database connection pool exhausted - - alert: DatabasePoolExhausted - expr: | - pg_stat_activity_max_connections_remaining < 5 - for: 1m - annotations: - summary: "Database connection pool nearly full" - - # Disk space low - - alert: DiskSpaceLow - expr: | - node_filesystem_avail_bytes{mountpoint="/"} / - node_filesystem_size_bytes{mountpoint="/"} < 0.1 - for: 5m - annotations: - summary: "Disk space low on {{ $labels.instance }}" - - # Queue backlog - - alert: QueueBacklog - expr: | - pg_partman_queue_depth > 1000 - for: 5m - annotations: - summary: "Message queue backlog detected" -``` - -### Grafana Dashboards - -Key dashboard panels to create: - -``` -Dashboard: Jan Server Overview -├── Requests/Second -├── Error Rate (%) -├── P50/P95/P99 Latency -├── Active Conversations -├── Database Connections -├── Cache Hit Rate -├── Message Queue Depth -├── Storage Usage -└── Cost Per 1M Tokens -``` - ---- - -## Distributed Tracing - -### OpenTelemetry Setup - -### Creating Custom Spans - -### Trace Context Propagation - ---- - -## Common Issues & Solutions - -### Issue 1: Database Connection Pool Exhausted - -**Symptoms:** -``` -Error: connection pool exhausted -Active connections: 50/50 -Queue depth: 100+ -``` - -**Root Causes:** -- Queries taking too long (connections held) -- Connection leak (not closing properly) -- Sudden traffic spike -- N+1 query problem - -**Diagnosis:** - -```sql --- Check active connections -SELECT datname, usename, count(*) -FROM pg_stat_activity -GROUP BY datname, usename; - --- Check long-running queries -SELECT query, query_start, now() - query_start as duration -FROM pg_stat_activity -WHERE state = 'active' -ORDER BY duration DESC; - --- Check waiting queries -SELECT query, query_start, now() - query_start as duration -FROM pg_stat_activity -WHERE state = 'idle in transaction' -ORDER BY duration DESC; -``` - -**Solutions:** - -### Issue 2: Out of Memory - -**Symptoms:** -``` -RSS Memory: 2.5GB (out of 3GB limit) -Swap usage increasing -Process killed: OOMKiller -``` - -**Root Causes:** -- Memory leak in application -- Large result set loading entirely in memory -- Cache growing unbounded -- Goroutine leak (Go services) - -**Diagnosis:** - -**Solutions:** - -### Issue 3: Message Queue Backlog - -**Symptoms:** -``` -Queue depth: 50000+ messages -Processing lag: 30+ minutes -Consumer lag not catching up -``` - -**Root Causes:** -- Consumer slower than producer -- Poison pill messages blocking queue -- Consumer crash/hang -- Message processing timeout - -**Solutions:** - -### Issue 4: High API Latency - -**Symptoms:** -``` -P99 latency: 10+ seconds -Some endpoints slow, others normal -Error rate increases under load -``` - -**Root Causes:** -- Slow database queries -- Cache miss storm (thundering herd) -- External API calls -- Upstream service degradation - -**Diagnosis:** - -**Solutions:** - -### Issue 5: Authentication Failures - -**Symptoms:** -``` -Keycloak connection errors -401 Unauthorized responses -Token validation timeouts -``` - -**Solutions:** - ---- - -## Performance Optimization - -### Database Query Optimization - -### Cache Strategy - -### Connection Pooling - ---- - -## Logging Strategies - -### Structured Logging - -### Log Levels - -### Log Aggregation with ELK Stack - -```yaml -# filebeat.yml -filebeat.inputs: - - type: log - enabled: true - paths: - - /var/log/jan-server/*.log - json.message_key: message - json.keys_under_root: true - -output.elasticsearch: - hosts: ["localhost:9200"] - index: "jan-server-%{+yyyy.MM.dd}" - -processors: - - add_kubernetes_metadata: - in_cluster: true -``` - ---- - -## Capacity Planning - -### Resource Monitoring - -### Scaling Recommendations - -``` -Traffic Level CPU Memory Disk Scaling -───────────────────────────────────────────────────────────── -Low (< 100 req/s) 20-30% 30-40% 50GB 1 instance -Medium (100-500) 40-50% 40-50% 100GB 2-3 instances -High (500-2000) 60-70% 50-70% 500GB 4-8 instances -Very High (2000+) >70% >70% 1TB+ Horizontal + cache -``` - -### Cost Optimization - ---- - -## Incident Response - -### Runbook Example: Database Down - -```markdown -## Incident: Database Connection Lost - -### Detection -- Alert: `ServiceDown` for database -- Symptom: All APIs returning 500 errors - -### Immediate Actions (0-5 min) -1. Check database status: - ```bash - pg_isready -h localhost -p 5432 - ``` -2. Check database logs: - ```bash - docker logs jan-postgresql - ``` -3. If database is running, check connectivity from services: - ```bash - kubectl exec -it pod/jan-llm-api -- psql -c "SELECT 1" - ``` - -### Diagnosis (5-15 min) -- [ ] Is database process running? `ps aux | grep postgres` -- [ ] Is disk full? `df -h` -- [ ] Check system logs: `journalctl -n 50` -- [ ] Check network connectivity: `ping database-host` - -### Recovery Steps -1. **If disk full:** - - Clean old logs: `rm -rf /var/log/postgresql/*.log*` - - Check large tables: `SELECT schemaname, tablename, pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) FROM pg_tables ORDER BY pg_total_relation_size DESC LIMIT 10` - - Archive old data if applicable - -2. **If connection pool exhausted:** - - Check active connections: `SELECT count(*) FROM pg_stat_activity` - - Terminate idle connections: `SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE state = 'idle' AND query_start < now() - interval '1 hour'` - - Restart services: `kubectl rollout restart deployment/jan-llm-api` - -3. **If database corrupted:** - - Check integrity: `REINDEX DATABASE postgres` - - If severe, restore from backup - -### Escalation -- If not resolved in 15 min: Page on-call DBA -- If customer impact: Update status page - -### Post-Incident -- [ ] Root cause analysis meeting -- [ ] Add monitoring/alerting to prevent recurrence -- [ ] Update runbook with findings -``` - -### Alert Severity Levels - ---- - -## Summary Checklist - -- [ ] Health checks configured for all services -- [ ] Prometheus scraping all metrics -- [ ] Grafana dashboards displaying key metrics -- [ ] Alert rules configured for critical issues -- [ ] Logging to centralized system -- [ ] Distributed tracing enabled -- [ ] Runbooks documented for common incidents -- [ ] On-call rotation established -- [ ] Regular chaos engineering exercises -- [ ] Quarterly capacity planning review - -See [MCP Custom Tools Guide](./mcp-custom-tools) for tool-specific monitoring and [Webhooks Guide](./webhooks) for webhook health checks. diff --git a/apps/platform/content/docs/guides/monitoring.mdx b/apps/platform/content/docs/guides/monitoring.mdx deleted file mode 100644 index 9b4c371f..00000000 --- a/apps/platform/content/docs/guides/monitoring.mdx +++ /dev/null @@ -1,358 +0,0 @@ ---- -title: "Observability & Monitoring Stack" ---- - -# Observability & Monitoring Stack - -This document describes the optional observability stack for Jan Server, which provides comprehensive monitoring, metrics, and distributed tracing capabilities. - -## Overview - -The monitoring stack is completely optional and runs separately from the main Jan Server services. It consists of: - -- **OpenTelemetry Collector**: Telemetry data collection and forwarding -- **Prometheus**: Metrics storage and querying -- **Jaeger**: Distributed tracing backend -- **Grafana**: Unified visualization dashboard - -## Quick Start - -### Start Monitoring Stack -```bash -make monitor-up -``` - -This command will: -1. Start all monitoring services (Prometheus, Jaeger, Grafana, OpenTelemetry Collector) -2. Display access URLs for each dashboard -3. Run in the background - -### Access Dashboards - -- **Grafana** (Unified Dashboard): http://localhost:3331 - - Username: `admin` - - Password: `admin` - - Pre-configured with Prometheus and Jaeger datasources - -- **Prometheus** (Metrics): http://localhost:9090 - - Direct PromQL queries - - Service discovery status - - Target health monitoring - -- **Jaeger** (Traces): http://localhost:16686 - - Distributed trace search - - Service dependency graph - - Performance analysis - -### Stop Monitoring Stack -```bash -# Stop but keep data -make monitor-down - -# Stop and remove all data volumes (fresh start) -make monitor-clean -``` - -### View Logs -```bash -make monitor-logs -``` - -## Architecture - -``` -+-------------------------------------------------------------+ -| Jan Server Services | -| (llm-api, mcp-tools, etc.) | -+----------------+--------------------------------------------+ - | OpenTelemetry Protocol (OTLP) - | Ports: 4318 (HTTP), 4317 (gRPC) - v -+-------------------------------------------------------------+ -| OpenTelemetry Collector | -| - Receives metrics and traces from services | -| - Processes and enriches telemetry data | -| - Exports to Prometheus (metrics) and Jaeger (traces) | -| - Uses OTLP exporter for Jaeger (not deprecated Jaeger) | -+------------+------------------------------+-----------------+ - | | - | Metrics | Traces (OTLP) - v v -+------------------------+ +--------------------------------+ -| Prometheus | | Jaeger | -| - Time-series DB | | - Trace storage | -| - 15s scrape interval | | - Service dependency graph | -| - PromQL queries | | - Performance insights | -+------------+-----------+ +------------+-------------------+ - | | - +--------------+---------------+ - v - +------------------------+ - | Grafana | - | - Unified dashboards | - | - Metrics + Traces | - | - Alerting | - +------------------------+ -``` - -## Configuration - -### Environment Variables - -Set these in your `.env.docker` or `.env.local` file: - -```bash -# Prometheus -PROMETHEUS_PORT=9090 - -# Jaeger -JAEGER_UI_PORT=16686 - -# Grafana -GRAFANA_PORT=3331 -GRAFANA_ADMIN_USER=admin -GRAFANA_ADMIN_PASSWORD=admin - -# OpenTelemetry -OTEL_GRPC_PORT=4317 -OTEL_HTTP_PORT=4318 -``` - -### Enable Telemetry in Services - -To send metrics and traces from Jan Server services: - -```bash -# In llm-api environment -OTEL_ENABLED=true -OTEL_SERVICE_NAME=llm-api -OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 -``` - -### Prometheus Configuration - -The `monitoring/prometheus.yml` file defines scrape targets: - -```yaml -scrape_configs: - - job_name: 'otel-collector' - static_configs: - - targets: ['otel-collector:8889'] - - - job_name: 'llm-api' - static_configs: - - targets: ['llm-api:8080'] - - - job_name: 'mcp-tools' - static_configs: - - targets: ['mcp-tools:8091'] -``` - -### Grafana Datasources - -Datasources are auto-provisioned from `monitoring/grafana/provisioning/datasources/datasources.yml`: - -- **Prometheus**: Default datasource for metrics -- **Jaeger**: Datasource for distributed traces - -## Usage - -### Viewing Metrics in Prometheus - -1. Navigate to http://localhost:9090 -2. Use the "Graph" tab for queries -3. Example PromQL queries: - ```text - # Request rate - rate(http_requests_total[5m]) - - # Error rate - rate(http_requests_total{status=~"5.."}[5m]) - - # Response time (95th percentile) - histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) - ``` - -### Viewing Traces in Jaeger - -1. Navigate to http://localhost:16686 -2. Select a service (e.g., `llm-api`) -3. Search for traces by: - - Time range - - Duration - - Tags -4. Click on a trace to view: - - Span timeline - - Service dependencies - - Tags and logs - -### Creating Grafana Dashboards - -1. Navigate to http://localhost:3331 (admin/admin) -2. Click "+" -> "Create Dashboard" -3. Add panels with queries from Prometheus or Jaeger -4. Save the dashboard - -To persist dashboards: -1. Export as JSON -2. Save to `monitoring/grafana/provisioning/dashboards/json/` -3. Restart Grafana: `make monitor-down && make monitor-up` - -## Data Persistence - -The monitoring stack uses Docker volumes for data persistence: - -- `prometheus-data`: Stores metrics time-series data -- `grafana-data`: Stores dashboards, users, and settings - -### Backup Data - -```bash -# Backup Prometheus data -docker run --rm -v jan-server_prometheus-data:/data -v $(pwd):/backup alpine tar czf /backup/prometheus-backup.tar.gz -C /data. - -# Backup Grafana data -docker run --rm -v jan-server_grafana-data:/data -v $(pwd):/backup alpine tar czf /backup/grafana-backup.tar.gz -C /data. -``` - -### Restore Data - -```bash -# Restore Prometheus data -docker run --rm -v jan-server_prometheus-data:/data -v $(pwd):/backup alpine sh -c "cd /data && tar xzf /backup/prometheus-backup.tar.gz" - -# Restore Grafana data -docker run --rm -v jan-server_grafana-data:/data -v $(pwd):/backup alpine sh -c "cd /data && tar xzf /backup/grafana-backup.tar.gz" -``` - -## Troubleshooting - -### Monitoring Stack Won't Start - -```bash -# Check if services are running -docker compose -f docker/observability.yml ps - -# View logs -make monitor-logs - -# Restart with fresh data -make monitor-clean && make monitor-up -``` - -### No Metrics in Prometheus - -1. Check if OpenTelemetry Collector is running: - ```bash - docker compose -f docker/observability.yml ps otel-collector - ``` - -2. Verify Prometheus targets are healthy: - - Navigate to http://localhost:9090/targets - - All targets should show "UP" status - -3. Ensure services are exporting metrics: - - Set `OTEL_ENABLED=true` in service environment - - Restart the service - -### No Traces in Jaeger - -1. Check Jaeger is receiving data: - ```bash - make monitor-logs | grep jaeger - ``` - -2. Verify OpenTelemetry Collector is exporting to Jaeger: - ```bash - make monitor-logs | grep "jaeger.*exporter" - ``` - -3. Ensure services are generating traces: - - Check service logs for trace IDs - - Verify OTLP endpoint is correct - -### Grafana Datasources Not Working - -1. Check datasource configuration: - - Login to Grafana - - Go to Configuration -> Data Sources - - Test each datasource - -2. Verify provisioning: - ```bash - docker compose -f docker/observability.yml exec grafana ls -la /etc/grafana/provisioning/datasources - ``` - -3. Restart Grafana: - ```bash - docker compose -f docker/observability.yml restart grafana - ``` - -## Advanced Configuration - -### Custom Prometheus Retention - -Edit `docker/observability.yml`: - -```yaml -prometheus: - command: - - '--storage.tsdb.retention.time=30d' # Keep data for 30 days - - '--storage.tsdb.retention.size=10GB' # Max 10GB storage -``` - -### Custom Grafana Plugins - -Edit `docker/observability.yml`: - -```yaml -grafana: - environment: - GF_INSTALL_PLUGINS: 'grafana-clock-panel,grafana-simple-json-datasource' -``` - -### Enable Jaeger Sampling - -Edit `docker/observability.yml`: - -```yaml -jaeger: - environment: - COLLECTOR_OTLP_ENABLED: "true" - SAMPLING_STRATEGIES_FILE: /etc/jaeger/sampling.json - volumes: - -./docs/jaeger-sampling.json:/etc/jaeger/sampling.json:ro -``` - -## Production Recommendations - -1. **Change default Grafana password**: - ```bash - GRAFANA_ADMIN_PASSWORD= - ``` - -2. **Configure retention policies**: - - Prometheus: Set appropriate retention based on storage - - Jaeger: Configure sampling to reduce data volume - -3. **Set up alerting**: - - Configure Prometheus alert rules - - Set up Grafana alert notifications (email, Slack, etc.) - -4. **Secure access**: - - Use reverse proxy (nginx/traefik) with TLS - - Implement authentication/authorization - - Restrict network access to monitoring ports - -5. **Scale for production**: - - Use external storage for Prometheus (remote write) - - Use production-grade Jaeger backend (Elasticsearch, Cassandra) - - Enable Grafana HA mode - -## Resources - -- [Prometheus Documentation](https://prometheus.io/docs/) -- [Grafana Documentation](https://grafana.com/docs/) -- [Jaeger Documentation](https://www.jaegertracing.io/docs/) -- [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/) -- [PromQL Cheat Sheet](https://promlabs.com/promql-cheat-sheet/) diff --git a/apps/platform/content/docs/guides/prompt-orchestration.mdx b/apps/platform/content/docs/guides/prompt-orchestration.mdx deleted file mode 100644 index 6f4248d8..00000000 --- a/apps/platform/content/docs/guides/prompt-orchestration.mdx +++ /dev/null @@ -1,546 +0,0 @@ ---- -title: "Prompt Orchestration" ---- - -# Prompt Orchestration - -## Overview - -The Prompt Orchestration system is a pipeline component within the LLM API service that dynamically composes and enhances prompts before they are sent to inference providers. It applies conditional modules based on context, user preferences, conversation history, and database-driven templates. - -**Architecture Decision**: Prompt orchestration is implemented as a **processor within the LLM API service**, not as an isolated microservice. This gives you dynamic control at runtime to add memory, tools, templates, customize tone, and assemble final prompts automatically. - ---- - -## What is a Prompt Orchestration Processor? - -A **Prompt Orchestration Processor** is a processing layer within LLM API that: - -1. Takes a user's raw input (before it reaches the inference engine) -2. Checks conditions (flags, context, user settings, memory, etc.) -3. Composes a final prompt programmatically by applying conditional modules -4. Passes that composed prompt to the inference provider (vLLM or remote) - -The processor sits in the request pipeline within `llm-api`, between the HTTP handler and the inference provider client. - ---- - -## Architecture - -``` -HTTP Request (POST /v1/chat/completions) - ↓ -Gin Handler (llm-api/internal/interfaces/httpserver/handlers/chathandler) - ↓ -1. Conversation Context Loading - - Get or create conversation - - Load conversation items - - Load project instructions - ↓ -2. Memory Loading - - Collect prompt memory from headers/metadata - - Load memory context via memory-tools service - ↓ -3. Provider Selection - - Select provider model based on request - - Load model catalog for context length - ↓ -4. Media Resolution - - Resolve jan_* media placeholders - ↓ -5. Project Instruction Injection - - Prepend project instruction as first system message - ↓ -6. Prompt Orchestration Processor - - Build prompt context with preferences - - Apply conditional modules in priority order - - Return enhanced messages - ↓ -7. Context Trimming - - Trim messages to fit model context length - ↓ -Inference Provider Client (internal/infrastructure/inference) - ↓ -vLLM or Remote Provider -``` - -### Package Structure - -``` -services/llm-api/internal/domain/prompt/ -├── types.go # Core interfaces and types -├── modules.go # Built-in module implementations -├── deep_research_module.go # Deep Research module -├── processor.go # Main processor implementation -└── processor_test.go # Unit tests -``` - ---- - -## Features & Capabilities - -### What the Processor Can Do - -The processor can automatically attach optional modules as part of the LLM API request pipeline: - -#### Deep Research -Inject specialized research prompts for comprehensive analysis when `deep_research: true` is set. - -#### User Profile Personalization -Inject user profile settings including: -- Base style (concise, friendly, professional) -- Custom instructions -- Nickname, occupation, and personal context - -#### Memory -If user enables memory, insert memory hints/preferences into prompt. - -#### Tool Usage -Conditionally include instructions like: -- "use the retrieval tool when needed" -- "use the calculator tool if numbers appear" - -#### Templates / Prompt Patterns -For example: -- Chain-of-Thought structure -- Code assistant guidance -- Output format -- "First think step-by-step, then answer" - -#### Project Instructions -Inject project-specific instructions with highest priority. - -#### Timing Context -Add current date and AI assistant introduction. - ---- - -## Built-in Modules - -The processor includes several built-in modules that are automatically applied based on context: - -### -20. Deep Research Module (Conditional) -- **Purpose**: Injects comprehensive research prompts for deep analysis -- **Activation**: Enabled when `deep_research: true` in preferences -- **Adds**: Research methodology and comprehensive analysis instructions -- **Priority**: -20 (runs before all other modules) - -### -15. Timing Module (Always Active) -- **Purpose**: Ensures a base system prompt with current date is present -- **Activation**: Always registered when prompt orchestration is enabled -- **Adds**: AI assistant intro and current date to the system prompt -- **Priority**: -15 - -### -10. Project Instruction Module (Conditional) -- **Purpose**: Injects project-specific instructions with highest priority -- **Activation**: When conversation has a linked project with instructions -- **Adds**: Project instructions as first system message with priority note -- **Priority**: -10 - -### 5. User Profile Module (Conditional) -- **Purpose**: Injects user profile personalization settings -- **Activation**: When user has profile settings (base style, custom instructions, nickname, etc.) -- **Adds**: Style preferences, custom instructions, and user context -- **Priority**: 5 - -### 10. Memory Module (Optional) -- **Purpose**: Injects user-specific memory/preferences into prompts -- **Activation**: Enabled via `PROMPT_ORCHESTRATION_MEMORY=true` and memory items present -- **Adds**: Memory hints stitched into the system prompt -- **Priority**: 10 - -### 20. Tool Instructions Module (Optional) -- **Purpose**: Adds instructions for tool usage -- **Activation**: `PROMPT_ORCHESTRATION_TOOLS=true` and tools present in request -- **Adds**: Tool selection and usage guidelines -- **Priority**: 20 - -### 30. Code Assistant Module (Template-Gated) -- **Purpose**: Enhances prompts for code-related questions -- **Activation**: `PROMPT_ORCHESTRATION_TEMPLATES=true` and code keywords detected -- **Adds**: Code formatting guidelines, best practices, error handling tips -- **Priority**: 30 - -### 40. Chain-of-Thought Module (Template-Gated) -- **Purpose**: Encourages step-by-step reasoning for complex questions -- **Activation**: `PROMPT_ORCHESTRATION_TEMPLATES=true` and reasoning signals detected -- **Adds**: Instructions to break down problems and think systematically -- **Priority**: 40 - ---- - -## Configuration - -### Environment Variables - -| Variable | Default | Description | -|----------|---------|-------------| -| `PROMPT_ORCHESTRATION_ENABLED` | `false` | Enable/disable the processor | -| `PROMPT_ORCHESTRATION_MEMORY` | `false` | Enable memory injection | -| `PROMPT_ORCHESTRATION_TEMPLATES` | `false` | Enable template-based prompts (CoT + code assistant) | -| `PROMPT_ORCHESTRATION_TOOLS` | `false` | Enable tool usage instructions | - -### YAML Configuration - -In `config/defaults.yaml`: - -```yaml -services: - llm_api: - prompt_orchestration: - enabled: false - enable_memory: false - enable_templates: false - enable_tools: false -``` - -### Wire Integration - -The processor is integrated via dependency injection in `services/llm-api/internal/domain/provider.go`: - -```go -// ProvidePromptProcessor creates the prompt processor with all modules including Deep Research -func ProvidePromptProcessor( - config prompt.ProcessorConfig, - log zerolog.Logger, - templateService *prompttemplate.Service, -) *prompt.ProcessorImpl { - processor := prompt.NewProcessorWithTemplateService(config, log, templateService) - - // Register Deep Research module if prompt orchestration is enabled - if config.Enabled && templateService != nil { - processor.RegisterModule(prompt.NewDeepResearchModule(templateService)) - } - - return processor -} -``` - ---- - -## Implementation Details - -### Module Interface - -Each module implements the `Module` interface: - -```go -type Module interface { - Name() string - ShouldApply(ctx context.Context, promptCtx *Context, messages []openai.ChatCompletionMessage) bool - Apply(ctx context.Context, promptCtx *Context, messages []openai.ChatCompletionMessage) ([]openai.ChatCompletionMessage, error) -} -``` - -### Context Structure - -The prompt context contains all information needed for module decisions: - -```go -type Context struct { - UserID uint - ConversationID string - Language string - Preferences map[string]interface{} - Memory []string - ProjectInstruction string - AppliedModules []string - Profile *usersettings.ProfileSettings -} -``` - -### Processing Flow - -1. **Context Building**: Create a `prompt.Context` with user ID, conversation ID, preferences, memory, project instruction, and user profile -2. **Module Evaluation**: Each registered module checks if it should apply via `ShouldApply()` -3. **Module Application**: Applicable modules modify messages via `Apply()` in priority order -4. **Result**: Enhanced messages are passed to the inference provider - -### Module Priority System - -Modules are executed in priority order to ensure correct composition: -- **Priority -20**: Deep Research Module (comprehensive research prompts) -- **Priority -15**: Timing Module (creates base system prompt with date) -- **Priority -10**: Project Instruction Module (highest priority instructions) -- **Priority 5**: User Profile Module (personalization settings) -- **Priority 10**: Memory Module (adds user context) -- **Priority 20**: Tool Instructions (adds tool capabilities) -- **Priority 30**: Code Assistant (adds code-specific guidance) -- **Priority 40**: Chain-of-Thought (adds reasoning structure) - ---- - -## Usage in Chat Handler - -The processor is integrated into the chat completion flow in `chat_handler.go`: - -```go -// Apply prompt orchestration (if enabled) -if h.promptProcessor != nil { - observability.AddSpanEvent(ctx, "processing_prompts") - - preferences := make(map[string]interface{}) - if len(request.Tools) > 0 || request.ToolChoice != nil { - preferences["use_tools"] = true - } - if persona := strings.TrimSpace(reqCtx.GetHeader("X-Prompt-Persona")); persona != "" { - preferences["persona"] = persona - } - if persona := strings.TrimSpace(reqCtx.Query("persona")); persona != "" { - preferences["persona"] = persona - } - - // Pass deep_research flag to prompt orchestration - if request.DeepResearch != nil && *request.DeepResearch { - preferences["deep_research"] = true - } - - var profileSettings *usersettings.ProfileSettings - if userSettings != nil { - profileSettings = &userSettings.ProfileSettings - } - - promptCtx := &prompt.Context{ - UserID: userID, - ConversationID: conversationID, - Language: strings.TrimSpace(reqCtx.GetHeader("Accept-Language")), - Preferences: preferences, - Memory: loadedMemory, - ProjectInstruction: projectInstruction, - Profile: profileSettings, - } - - processedMessages, processErr := h.promptProcessor.Process(ctx, promptCtx, request.Messages) - if processErr != nil { - // Continue with original messages - } else { - request.Messages = processedMessages - if len(promptCtx.AppliedModules) > 0 { - reqCtx.Header("X-Applied-Prompt-Modules", strings.Join(promptCtx.AppliedModules, ",")) - } - observability.AddSpanEvent(ctx, "prompts_processed") - } -} -``` - ---- - -## Template Service Integration - -Modules can load prompts from the database via the `prompttemplate.Service`: - -```go -// Try to fetch template from database and render with variables -if m.templateService != nil { - template, err := m.templateService.GetByKey(ctx, prompttemplate.TemplateKeyTiming) - if err == nil && template != nil && template.IsActive { - rendered, renderErr := m.templateService.RenderTemplate(ctx, prompttemplate.TemplateKeyTiming, map[string]any{ - "CurrentDate": currentDate, - }) - if renderErr == nil { - timingText = rendered - } - } -} - -// Fallback to hardcoded text if template not loaded -if timingText == "" { - timingText = "You are Jan, a helpful AI assistant..." -} -``` - -### Available Template Keys - -| Template Key | Module | Variables | -|-------------|--------|-----------| -| `timing` | TimingModule | `CurrentDate` | -| `user_profile` | UserProfileModule | `BaseStyle`, `CustomInstructions`, `NickName`, `Occupation`, `MoreAboutYou` | -| `memory` | MemoryModule | `MemoryItems` | -| `tool_instructions` | ToolInstructionsModule | `ToolDescriptions` | -| `code_assistant` | CodeAssistantModule | (none) | -| `chain_of_thought` | ChainOfThoughtModule | (none) | -| `deep_research` | DeepResearchModule | (none) | - ---- - -## Example Transformations - -### Before Processing -```json -{ - "messages": [ - {"role": "user", "content": "How do I implement binary search in Go?"} - ] -} -``` - -### After Processing -*With Timing + User Profile + Code Assistant modules applied:* - -```json -{ - "messages": [ - { - "role": "system", - "content": "You are Jan, a helpful AI assistant. Jan is trained by Menlo Research (https://www.menlo.ai).\nToday is: December 16, 2025.\nAlways treat this as the current date.\n\nUser-level settings are preferences for style and context. If they ever conflict with explicit project or system instructions, always follow the project or system instructions.\n\nUse a friendly, warm, and encouraging tone while staying helpful.\n\nWhen providing code assistance:\n1. Provide clear, well-commented code.\n2. Explain your approach and reasoning.\n3. Include error handling where appropriate.\n4. Follow best practices and conventions.\n5. Suggest testing approaches when relevant.\n6. Respect project instructions and user constraints; never violate them to simplify code." - }, - {"role": "user", "content": "How do I implement binary search in Go?"} - ] -} -``` - -### With Project Instructions - -When a conversation is linked to a project with instructions: - -```json -{ - "messages": [ - { - "role": "system", - "content": "Always respond in JSON format. Use TypeScript conventions.\n\nProject priority: These project-specific instructions have the highest priority. If any user settings, style preferences, or other guidance conflict with these project instructions, you must follow the project instructions." - }, - { - "role": "system", - "content": "You are Jan, a helpful AI assistant...\n\nUse a friendly, warm, and encouraging tone..." - }, - {"role": "user", "content": "Create a function to validate email addresses"} - ] -} -``` - ---- - -## Disabling Modules - -Users can disable specific modules via preferences: - -```go -promptCtx := &prompt.Context{ - Preferences: map[string]interface{}{ - "disable_modules": []string{"chain_of_thought", "code_assistant"}, - }, -} -``` - -Or via the helper function: -```go -promptCtx = prompt.WithDisabledModules(promptCtx, []string{"memory"}) -``` - -Supported formats for `disable_modules`: -- Comma-separated string: `"chain_of_thought,code_assistant"` -- String slice: `[]string{"chain_of_thought", "code_assistant"}` -- Interface slice: `[]interface{}{"chain_of_thought", "code_assistant"}` - ---- - -## Observability - -The processor emits: -- **OTEL events**: `processing_prompts`, `prompts_processed` -- **Logs**: Applied module list, processing errors, module priority order -- **HTTP header**: `X-Applied-Prompt-Modules` (comma-separated) for debugging - -Example log output: -```json -{ - "level": "debug", - "component": "prompt-processor", - "conversation_id": "conv-123", - "applied_modules": ["timing", "user_profile", "memory", "code_assistant"], - "message": "applied prompt orchestration modules" -} -``` - ---- - -## Testing - -Run the test suite: - -```bash -cd services/llm-api -go test ./internal/domain/prompt/... -v -``` - -Tests cover: -- Individual module behavior -- Module conditional logic -- Processor integration -- Configuration handling -- Module priority ordering -- Template service integration - ---- - -## Future Enhancements - -Potential additions to the processor: - -1. **Template Library**: Pre-built templates for common tasks (writing, analysis, translation) -2. **Dynamic Persona**: Adjust assistant personality based on context -3. **Language Detection**: Automatically adapt to user's language -4. **Safety Filters**: Add content moderation and safety rules -5. **A/B Testing**: Compare different prompt strategies -6. **Custom Module Registry**: Allow users to register custom modules -7. **Module Composition Rules**: Define dependencies and conflicts between modules -8. **Prompt Versioning**: Track and version prompt templates -9. **Performance Optimization**: Cache compiled prompts for common scenarios - ---- - -## Related Documentation - -- [Data Flow Reference](../architecture/data-flow) -- [LLM API Documentation](../api/llm-api/) -- [Development Guide](./development) -- [Testing Guide](./testing) - ---- - -## Troubleshooting - -### Modules Not Applying - -**Check:** -1. Is `PROMPT_ORCHESTRATION_ENABLED=true`? -2. Are specific module flags enabled (`MEMORY`, `TEMPLATES`, `TOOLS`)? -3. Does the module's `ShouldApply()` logic match your request? -4. Check logs for `X-Applied-Prompt-Modules` header - -### Module Order Issues - -**Solution:** -Modules execute in priority order (-20, -15, -10, 5, 10, 20, 30, 40). Deep Research runs first, then Timing, then Project Instructions. - -### Memory Not Loading - -**Check:** -1. Is memory provided via `X-Prompt-Memory` header or conversation metadata? -2. Is `promptCtx.Memory` populated with items? -3. Is `PROMPT_ORCHESTRATION_MEMORY=true`? -4. Is `MEMORY_ENABLED=true` for the memory-tools integration? - -### User Profile Not Applying - -**Check:** -1. Does the user have profile settings configured? -2. Is at least one profile field non-empty (BaseStyle, CustomInstructions, NickName, etc.)? -3. Is the `user_profile` module not in the disabled list? - -### Template Not Loading from Database - -**Check:** -1. Is the template service properly initialized? -2. Is the template active (`is_active: true`)? -3. Check logs for template loading errors -4. Fallback prompts will be used if template fails - -### Performance Concerns - -**Optimization:** -- Modules are sorted once during processor initialization -- Each module only applies if `ShouldApply()` returns true -- Template service caches templates -- Consider disabling unused modules via environment variables diff --git a/apps/platform/content/docs/guides/services-template.mdx b/apps/platform/content/docs/guides/services-template.mdx deleted file mode 100644 index b7492afd..00000000 --- a/apps/platform/content/docs/guides/services-template.mdx +++ /dev/null @@ -1,25 +0,0 @@ ---- -title: "Service Template Overview" ---- - -# Service Template Overview - -The `services/template-api` directory contains a production-ready skeleton for new Jan microservices. Highlights: - -- Go module with config/logger/observability/http packages mirroring established patterns. -- GORM/PostgreSQL wiring (connection pool, migrations, seed data, repository example). -- Optional Keycloak JWT guard controlled via `AUTH_ENABLED`. -- Makefile + Dockerfile for local dev and CI. -- Wire entrypoint plus example env and docs. -- Use `jan-cli dev scaffold ` to copy the template with placeholders replaced. - -## Getting Started -1. Run `jan-cli dev scaffold my-service` (or copy `services/template-api` manually). -2. Update `go.mod`, the service section inside `.env.template`, and `cmd/server/server.go` with your service-specific names and dependencies. -3. Configure the database DSN (`DB_POSTGRESQL_WRITE_DSN`) and run `go run ./cmd/server` once so migrations seed the database. -4. Decide whether to enable JWT auth (`AUTH_ENABLED`, `AUTH_ISSUER`, `ACCOUNT`, `AUTH_JWKS_URL`). -5. Register your handlers inside `internal/interfaces/httpserver`. -6. Add domain packages and migrations as needed. -7. Update root `.env.template`, README, and deployment manifests to include your service. - -This guide provides a detailed checklist covering both greenfield and migration workflows for creating new services. diff --git a/apps/platform/content/docs/guides/testing.mdx b/apps/platform/content/docs/guides/testing.mdx deleted file mode 100644 index ebe4ab2d..00000000 --- a/apps/platform/content/docs/guides/testing.mdx +++ /dev/null @@ -1,510 +0,0 @@ ---- -title: "Testing Guide" ---- - -# Testing Guide - -**Last Updated**: January 2025 - -How to test Jan Server on Windows, Linux, and macOS. - ---- - -## Quick Summary - -Jan Server works on all major platforms: -- **Windows** - PowerShell scripts and build tools -- **Linux** - Full Docker support -- **macOS** - Full Docker support - -### What We Test - -- Command-line tools (jan-cli) -- Build process (Makefile) -- Docker containers -- Authentication (tokens, API keys) -- Cross-platform compatibility - ---- - -## Quick Start - -### Manual Testing - -**Unix/Linux/macOS:** -```bash -# Basic jan-cli commands -./jan-cli.sh --help -./jan-cli.sh dev setup -./jan-cli.sh config generate -./jan-cli.sh config validate -./jan-cli.sh config show -./jan-cli.sh service list -./jan-cli.sh swagger generate --service llm-api - -# Makefile targets -make setup -make build-llm-api -make build-media-api -make build-mcp -make clean-build -``` - -**Windows:** -```powershell - -### Windows - -**Supported:** -- OK jan-cli commands (all) -- OK Makefile build targets -- OK Configuration management -- OK Local Docker Desktop - -**Not Supported in CI:** -- [X] Docker integration (GitHub Actions limitation) -- Docker tests run on Ubuntu CI instead - -**Shell:** PowerShell 5.1+ or Git Bash - -### Linux (Ubuntu, Debian, etc.) - -**Supported:** -- OK jan-cli commands (all) -- OK Makefile targets (all) -- OK Docker integration (native) -- OK Full authentication testing - -**Shell:** Bash 4.0+ - -### macOS - -**Supported:** -- OK jan-cli commands (all) -- OK Makefile targets (all) -- OK Docker integration (via Docker Desktop or Colima) - -**Limitations in CI:** -- Docker setup on GitHub Actions runners is optional (may fail) -- Primary Docker testing happens on Ubuntu -- macOS CI focuses on CLI/build verification - -**Shell:** Bash 3.2+ or Zsh - ---- - -## Integration Testing - -### jan-cli api-test Collections - -**Authentication Tests:** -# Basic jan-cli commands -.\jan-cli.ps1 --help -.\jan-cli.ps1 dev setup -.\jan-cli.ps1 config generate -.\jan-cli.ps1 config validate -.\jan-cli.ps1 config show -.\jan-cli.ps1 service list -.\jan-cli.ps1 swagger generate --service llm-api - -# Makefile targets (requires Git Bash or WSL) -make setup -make build-llm-api -make build-media-api -make build-mcp -make clean-build -``` - ---- - -## Manual Testing - -### Testing Checklist - -#### jan-cli Commands (All Platforms) - -| Command | Windows | Linux | macOS | Notes | -|---------|---------|-------|-------|-------| -| `jan-cli --help` | OK | OK | OK | Shows all commands | -| `jan-cli dev setup` | OK | OK | OK | Creates directories, networks,.env | -| `jan-cli config generate` | OK | OK | OK | Generates schemas and defaults.yaml | -| `jan-cli config validate` | OK | OK | OK | Validates YAML configuration | -| `jan-cli config show` | OK | OK | OK | Displays merged configuration | -| `jan-cli config export --format env` | OK | OK | OK | Exports as environment variables | -| `jan-cli service list` | OK | OK | OK | Lists all services with ports | -| `jan-cli swagger generate --service llm-api` | OK | OK | OK | Generates OpenAPI docs | - -#### Makefile Targets (All Platforms) - -| Target | Windows | Linux | macOS | Notes | -|--------|---------|-------|-------|-------| -| `make setup` | OK | OK | OK | Delegates to jan-cli dev setup | -| `make config-generate` | OK | OK | OK | Uses jan-cli config generate | -| `make build-llm-api` | OK | OK | OK | Cross-platform build | -| `make build-media-api` | OK | OK | OK | Cross-platform build | -| `make build-mcp` | OK | OK | OK | Cross-platform build | -| `make clean-build` | OK | OK | OK | Platform-specific cleanup | - ---- - -## Docker Testing - -### Full Stack Tests (Linux/macOS) - -**Authentication Tests:** -```bash -make test-auth -``` - -Tests: -- JWT token generation and validation -- API key authentication -- OAuth/OIDC flows with Keycloak -- Token refresh endpoint - -**Conversation Tests:** -```bash -make test-conversations -``` - -Tests: -- Create, read, update, delete conversations -- Message history -- Conversation metadata - -**Response API Tests:** -```bash -make test-response -``` - -**Media API Tests:** -```bash -make test-media -``` - -**MCP Integration Tests:** -```bash -make test-mcp-integration -``` - -### Docker Setup by Platform - -#### Linux (Ubuntu/Debian) - -```bash -# Install Docker Engine -curl -fsSL https://get.docker.com -o get-docker.sh -sudo sh get-docker.sh - -# Add user to docker group -sudo usermod -aG docker $USER -newgrp docker - -# Install Docker Compose -sudo apt-get update -sudo apt-get install docker-compose-plugin -``` - -#### macOS - -**Option 1: Docker Desktop (Recommended for local development)** -- Download: https://www.docker.com/products/docker-desktop -- Pros: Easy to use, integrated Kubernetes -- Cons: Resource heavy, requires license for enterprise - -**Option 2: Colima (Lightweight alternative)** -```bash -# Install via Homebrew -brew install docker colima - -# Start with appropriate resources -colima start --cpu 4 --memory 8 --disk 100 - -# For CI/CD (conservative settings) -colima start \ - --cpu 2 \ - --memory 4 \ - --disk 20 \ - --vm-type=vz \ - --mount-type=virtiofs - -# Verify -docker info -docker compose version -``` - -#### Windows - -**Local Development:** -- Install Docker Desktop: https://www.docker.com/products/docker-desktop -- Requires WSL2 for best performance - -**GitHub Actions CI:** -- Docker not available on Windows runners -- Full stack tests run on Ubuntu instead -- Windows CI focuses on CLI and build tests - ---- - -## Platform-Specific Fixes - -### 1. Makefile Path Separators - -**Issue:** GitHub Actions Windows runners use Git bash, which doesn't support backslash paths. - -**Fix:** Use forward slashes universally (works in bash, PowerShell, and CMD): - -```makefile -build-llm-api: - @echo "Building LLM API..." -ifeq ($(OS),Windows_NT) - @cd services/llm-api && go build -o bin/llm-api.exe./cmd/server -else - @cd services/llm-api && go build -o bin/llm-api./cmd/server -endif -``` - -**Key Insight:** Forward slashes work on all platforms in modern shells. - -### 2. Clean Target Platform Commands - -**Issue:** `rm -rf` doesn't exist on Windows. - -**Fix:** Platform-specific directory removal: - -```makefile -clean-build: - @echo "Cleaning build artifacts..." -ifeq ($(OS),Windows_NT) - @if exist services\llm-api\bin rd /s /q services\llm-api\bin >nul 2>&1 - @if exist services\media-api\bin rd /s /q services\media-api\bin >nul 2>&1 - @if exist services\mcp-tools\bin rd /s /q services\mcp-tools\bin >nul 2>&1 -else - @rm -rf services/llm-api/bin - @rm -rf services/media-api/bin - @rm -rf services/mcp-tools/bin -endif -``` - -### 3. Auto-Rebuild Detection - -**Issue:** Wrapper scripts only checked `main.go`, missing changes in other source files. - -**Fix:** Check all `*.go` files recursively: - -**Windows (jan-cli.ps1):** -```powershell -$needsRebuild = $false -Get-ChildItem -Path $CLIDir -Filter "*.go" -Recurse | ForEach-Object { - if ($_.LastWriteTime -gt $binaryTime) { - $needsRebuild = $true - } -} -``` - -**Unix (jan-cli.sh):** -```bash -if find "$CLI_DIR" -name "*.go" -type f -newer "$BINARY" | grep -q.; then - echo "Detected changes in source files. Rebuilding..." -fi -``` - -### 4. Cross-Platform Sleep Commands - -**Issue:** Interactive setup needs platform-specific sleep commands. - -**Fix:** Platform detection in Go: - -```go -func execCommandSilent(name string, args...string) error { - cmd:= exec.Command(name, args...) - return cmd.Run() -} - -// Platform-specific sleep -if isWindows() { - execCommandSilent("powershell", "-Command", "Start-Sleep -Seconds 2") -} else { - execCommandSilent("sleep", "2") -} -``` - -### 5. Optional Docker Dependency - -**Issue:** Docker not available on Windows CI and macOS CI may have Colima startup failures. - -**Fix:** Made Docker checks optional in `tools/jan-cli/cmd_dev.go`: - -```go -dockerAvailable:= isDockerAvailable() -if !dockerAvailable { - fmt.Println("WARNING WARNING: Docker is not available") - fmt.Println(" Some features will be skipped") - // Continue with CLI-only setup -} - -// Conditionally create Docker network -if dockerAvailable { - createDockerNetwork() -} -``` - ---- - -## Troubleshooting - -### Permission Denied on jan-cli.sh - -```bash -chmod +x jan-cli.sh -``` - -### Docker Commands Fail - -```bash -# Check Docker is running -docker ps - -# Linux: Add user to docker group -sudo usermod -aG docker $USER -# Then logout and login - -# macOS: Start Docker Desktop or Colima -open -a Docker # Docker Desktop -colima start # Colima -``` - -### Go Not Found - -```bash -# Check Go installation -which go -go version - -# If not installed: -# Linux: sudo apt install golang-go -# macOS: brew install go -# Windows: Download from https://go.dev/dl/ -``` - -### Build Failures - -```bash -# Clean and rebuild -make clean-build -make build-llm-api - -# Check Go modules -go mod download -go mod verify -``` - -### Makefile Not Found (Windows) - -Make requires Git Bash or WSL on Windows: -- Install Git for Windows: https://git-scm.com/download/win -- Or use WSL: https://docs.microsoft.com/en-us/windows/wsl/install - -### Path Issues on Windows - -GitHub Actions Windows runners use Git bash. Ensure: -- Use forward slashes in Makefile paths -- Use `ifeq ($(OS),Windows_NT)` branches for Windows-specific commands -- Binary names include `.exe` extension for Windows - ---- - -## Best Practices - -### 1. Use jan-cli for Complex Operations - -Prefer `jan-cli` commands for: -- File system operations (creating directories, copying files) -- Interactive prompts -- Complex conditional logic - -**Why:** Go code is inherently cross-platform, while Makefile requires platform-specific branches. - -### 2. Use Makefile for Docker Operations - -Prefer `Makefile` for: -- Docker Compose commands (`up-infra`, `up-full`, `down`) -- Service orchestration -- Testing with jan-cli api-test -- Health checks - -**Why:** These operations are already cross-platform via Docker CLI. - -### 3. Test Both Systems - -When adding new functionality: -1. Test on Windows PowerShell first (most restrictive) -2. Test on Linux/macOS -3. Verify wrapper scripts auto-rebuild correctly -4. Check that both `jan-cli` and `make` interfaces work - -### 4. Manual Testing Before Pushing - -**Unix/Linux/macOS:** -```bash -# Test basic commands -./jan-cli.sh --help -./jan-cli.sh config validate -make build-llm-api -``` - -**Windows:** -```powershell -# Test basic commands -.\jan-cli.ps1 --help -.\jan-cli.ps1 config validate -make build-llm-api -``` - ---- - -## Summary - -### OK What Works - -- All core `jan-cli` commands on Windows, Linux, macOS -- Makefile build targets (cross-platform) -- Docker integration on Linux/macOS (and Windows local) -- Configuration management and validation -- Service orchestration and health checks - -### WARNING Platform Limitations - -**Windows:** -- Requires Git Bash or WSL for Makefile -- Binary names need `.exe` extension - -**macOS:** -- Requires Docker Desktop or Colima for Docker support - -**Linux:** -- Full compatibility, no known limitations - -### Docs Testing Coverage - -- OK CLI commands: All platforms -- OK Build targets: All platforms -- OK Docker integration: Linux/macOS/Windows (with Docker Desktop) -- OK Authentication: jan-cli api-test collections -- OK API integration: jan-cli api-test collections - ---- - -## Related Documentation - -- [Jan CLI Guide](./jan-cli) - Complete jan-cli command reference -- [Configuration System](../configuration/) - Configuration management -- [Development Guide](./development) - Local development setup -- [Architecture Overview](../architecture/) - System design - ---- - -**Tested Platforms:** -- OK Windows 11 PowerShell 5.1 -- OK Ubuntu 22.04+ -- OK macOS 14+ diff --git a/apps/platform/content/docs/guides/troubleshooting.mdx b/apps/platform/content/docs/guides/troubleshooting.mdx deleted file mode 100644 index c99dcaa6..00000000 --- a/apps/platform/content/docs/guides/troubleshooting.mdx +++ /dev/null @@ -1,473 +0,0 @@ ---- -title: "Troubleshooting Guide" ---- - -# Troubleshooting Guide - -Common problems and how to fix them. - -## Quick Fixes - -1. [Services won't start](#service-startup-issues) -2. [Database errors](#database-issues) -3. [API not responding](#api-issues) -4. [Login problems](#authentication-issues) -5. [Docker problems](#docker-issues) -6. [Kubernetes problems](#kubernetes-issues) -7. [Slow performance](#performance-issues) - -## Service Startup Issues - -### Port Already in Use - -**Error**: `Address already in use` or port conflict - -**Solutions**: -```bash -# Find what's using the port (Linux/macOS) -lsof -i:8080 -lsof -i:8082 -lsof -i:8285 -lsof -i:8091 - -# Find what's using the port (Windows) -netstat -ano | findstr:8080 -taskkill /PID /F - -# Or change ports in.env -HTTP_PORT=8081 -RESPONSE_API_PORT=8083 -MEDIA_API_PORT=8286 -``` - -### Service Crashes on Startup - -**Problem**: Container starts then immediately stops - -**How to diagnose**: -```bash -# Check the logs -make logs-api -make logs-mcp - -# Or use Docker directly -docker logs -``` - -**Common causes**: -- Missing required settings in .env file -- Database isn't ready yet -- Wrong configuration values - -**Fix**: -```bash -# Check your .env file has all required values -cat .env - -# Make sure database is running -make health-check - -# Restart the service -docker restart -``` - -### Services Won't Connect - -**Error**: `connection refused` or `cannot reach service` - -**Solutions**: -```bash -# Verify all services are running -docker ps - -# Check network connectivity -docker network ls -docker network inspect jan-server_default - -# Test connectivity between services -docker exec llm-api curl http://media-api:8285/healthz - -# Verify DNS resolution -docker exec llm-api nslookup media-api -``` - -## Database Issues - -### Database Connection Failed - -**Error**: `dial tcp: connect: connection refused` or `database connection error` - -**Solutions**: -```bash -# Check if PostgreSQL is running -docker ps | grep postgres - -# Check database credentials in.env -cat.env | grep DATABASE - -# Test connection -docker exec api-db psql -U jan_user -d jan_llm_api -c "SELECT 1" - -# Verify database exists -docker exec api-db psql -U postgres -l | grep jan -``` - -### Missing Database - -**Error**: `database "jan_llm_api" does not exist` - -**Solutions**: -```bash -# Create database -docker exec api-db psql -U postgres -c "CREATE DATABASE jan_llm_api" - -# Or run migrations -docker exec llm-api /app/llm-api migrate - -# Or with make -make db-migrate -``` - -### Table Migration Failed - -**Error**: Migration errors or schema mismatch - -**Solutions**: -```bash -# View migrations -docker exec api-db psql -U jan_user -d jan_llm_api \ - -c "SELECT name, version FROM schema_migrations" - -# Reset database (WARNING: destroys data!) -make db-reset - -# Re-migrate -make db-migrate -``` - -### Database Disk Full - -**Error**: `No space left on device` - -**Solutions**: -```bash -# Check disk usage -df -h - -# Clean up Docker volumes -docker system prune -a --volumes - -# Or manually remove volume -docker volume ls -docker volume rm -``` - -## API Issues - -### 401 Unauthorized - -**Error**: All requests return 401 - -**Solutions**: -```bash -# Get a guest token -curl -X POST http://localhost:8000/llm/auth/guest-login - -# Use token in requests -curl -H "Authorization: Bearer " \ - http://localhost:8000/v1/models - -# Check Keycloak is running -docker ps | grep keycloak - -# Verify token is valid -jwt decode # requires jwt-cli -``` - -### 404 Not Found - -**Error**: Endpoints return 404 - -**Check**: -```bash -# Verify service is running and healthy -curl http://localhost:8080/healthz # LLM API -curl http://localhost:8082/healthz # Response API -curl http://localhost:8285/healthz # Media API -curl http://localhost:8091/healthz # MCP Tools - -# Verify Kong is routing correctly -curl http://localhost:8000/ # Kong health -curl http://localhost:8000/v1/models # Via Kong -``` - -### Timeout Errors - -**Error**: `408 Request Timeout` or connection hangs - -**Solutions**: -```bash -# Increase timeout in .env -TOOL_EXECUTION_TIMEOUT=120s -MEDIA_REMOTE_FETCH_TIMEOUT=30s - -# Check service performance -docker stats llm-api media-api response-api mcp-tools - -# Look for stuck processes -make logs-api | grep -i timeout -``` - -### 500 Internal Server Error - -**Error**: Unexpected server error - -**Debug**: -```bash -# View detailed logs -docker logs --tail=100 -f - -# Check if service crashed -docker inspect - -# Restart service -docker restart - -# Or full restart -make down && make up-full -``` - -## Authentication Issues - -### Keycloak Not Responding - -**Error**: `Failed to connect to Keycloak` or auth endpoints fail - -**Solutions**: -```bash -# Check if Keycloak is running -docker ps | grep keycloak - -# Verify it's accessible -curl http://localhost:8085/admin - -# Check logs -docker logs keycloak - -# Restart Keycloak -docker restart keycloak -``` - -### Invalid JWT Token - -**Error**: Token is expired or invalid - -**Solutions**: -```bash -# Get new token -curl -X POST http://localhost:8000/llm/auth/guest-login - -# Check token expiration -jwt decode - -# For user auth, check credentials -curl -X POST http://localhost:8085/auth/realms/jan/protocol/openid-connect/token \ - -d "client_id=llm-api&grant_type=password&username=admin&password=admin" -``` - -## Docker Issues - -### Out of Memory - -**Error**: `OOMKilled` or memory errors - -**Solutions**: -```bash -# Check memory usage -docker stats - -# Increase Docker memory limit -# In Docker Desktop: Settings > Resources > Memory - -# Or reduce services running -make down -``` - -### Disk Space Low - -**Error**: `no space left on device` - -**Solutions**: -```bash -# Clean up unused images and volumes -docker system prune -a --volumes - -# Remove old containers -docker container prune - -# Check image sizes -docker images --format "table {{.Repository}}\t{{.Size}}" -``` - -### Network Issues - -**Error**: Services can't communicate - -**Solutions**: -```bash -# Verify network exists -docker network ls - -# Check network configuration -docker network inspect jan-server_default - -# Recreate network if needed -docker network rm jan-server_default -docker network create jan-server_default -``` - -## Kubernetes Issues - -### Pod Stuck in Pending - -**Error**: Pod stays in Pending state - -**Debug**: -```bash -# Check events -kubectl describe pod -n jan-server - -# Check node resources -kubectl top nodes - -# Check available storage -kubectl get pvc -n jan-server -``` - -### ImagePullBackOff - -**Error**: Can't pull image - -**Solutions**: -```bash -# Verify image exists -minikube image ls | grep jan - -# Rebuild image -cd services/llm-api -docker build -t jan/llm-api:latest. -minikube image load jan/llm-api:latest - -# Or update imagePullPolicy in values.yaml -imagePullPolicy: Never # For minikube -``` - -### Service Not Accessible - -**Error**: Service endpoints not working - -**Debug**: -```bash -# Check service exists -kubectl get svc -n jan-server - -# Port forward for access -kubectl port-forward -n jan-server svc/jan-server-llm-api 8080:8080 - -# Check service endpoints -kubectl get endpoints -n jan-server -``` - -## Performance Issues - -### High Memory Usage - -**Symptoms**: Services use lots of memory - -**Solutions**: -```bash -# Monitor memory -docker stats llm-api - -# Reduce batch size or concurrency -# Check service configuration in docker compose - -# Look for memory leaks in logs -docker logs llm-api | grep -i memory -``` - -### High CPU Usage - -**Symptoms**: CPU usage maxed out - -**Solutions**: -```bash -# Monitor CPU -docker stats - -# Reduce concurrent requests -# Set rate limits in configuration - -# Check for infinite loops or busy waits -docker logs llm-api | grep -i error -``` - -### Slow Responses - -**Symptoms**: API requests are slow - -**Solutions**: -```bash -# Check database performance -docker exec api-db psql -U jan_user -d jan_llm_api \ - -c "SELECT query, calls, total_time FROM pg_stat_statements ORDER BY total_time DESC LIMIT 10" - -# Enable query logging -# Set LOG_LEVEL=debug in.env - -# Use monitoring stack for traces -make monitor-up -# Visit http://localhost:16686 (Jaeger) -``` - -## Getting Help - -### Gathering Debug Information - -Before asking for help, collect this information: - -```bash -# System info -docker version -docker-compose version -go version -kubectl version - -# Service status -make health-check - -# Logs from all services -make logs > debug-logs.txt - -# Configuration (without secrets) -cat.env | grep -v _KEY | grep -v _PASSWORD > config.txt - -# Docker system status -docker system df -docker ps -a -``` - -### Requesting Support - -When reporting issues, include: -1. Error messages and logs -2. Steps to reproduce -3. Your environment (OS, Docker version, etc.) -4. Configuration (sanitized) -5. What you've already tried - -**Resources**: -- [GitHub Issues](https://github.com/janhq/jan-server/issues) -- [Discussions](https://github.com/janhq/jan-server/discussions) -- [Architecture Documentation](../architecture/) -- [Development Guide](./development) diff --git a/apps/platform/content/docs/guides/user-management-todo.mdx b/apps/platform/content/docs/guides/user-management-todo.mdx deleted file mode 100644 index 0e096757..00000000 --- a/apps/platform/content/docs/guides/user-management-todo.mdx +++ /dev/null @@ -1,850 +0,0 @@ ---- -title: "User Management & Feature Flags System - Implementation Todo" ---- - -# User Management & Feature Flags System - Implementation Todo - -## Overview - -Implement a comprehensive user management system with role-based access control (RBAC), group-based organization, and **group-level feature flags** for Jan Server. - -**Architecture Decision**: -- Use **Keycloak as the single source of truth** for users, roles, and groups -- **No local cache/sync** - Read directly from JWT claims -- **Feature flags stored in Keycloak group attributes** and included in JWT -- Jan Server only stores feature flag definitions and audit logs -- Users inherit ALL feature flags from ALL their groups (additive inheritance) - -## Technology Stack - -### Backend -- **Language**: Go (Golang) -- **Framework**: Gin (HTTP router) -- **Database**: PostgreSQL (audit logs & feature flag definitions only) -- **Keycloak Client**: `github.com/Nerzal/gocloak/v13` (Admin API for management operations only) -- **Authentication**: JWT validation via JWKS -- **Circuit Breaker**: `github.com/sony/gobreaker` (Keycloak resilience) - -### Frontend (Future) -- **Framework**: React or Next.js -- **UI Library**: shadcn/ui or Material-UI -- **State Management**: React Query (for cache management) -- **Tables**: TanStack Table (React Table) -- **Forms**: React Hook Form + Zod validation - -### Infrastructure -- **Identity Provider**: Keycloak (users, groups, roles, attributes) -- **API Gateway**: Kong (already in use) -- **JWT Validation**: JWKS endpoint from Keycloak -- **Observability**: Prometheus + Grafana (already in use) -## User Roles - -### Admin Role -- **Privileges**: Full system access, can manage all users and groups -- **Identification**: - - Keycloak realm role: `admin` OR - - Keycloak user attribute: `is_admin: true` -- **Source of Truth**: Keycloak realm roles -- **Capabilities**: - - Create, read, update, delete users (via Keycloak Admin API) - - Assign/revoke admin privileges - - Manage user groups (via Keycloak Admin API) - - Activate/deactivate user accounts - - View all system users - - **Manage group feature flags** (admin-only) - -### User Role -- **Privileges**: Standard platform access -- **Identification**: Default role for authenticated users -- **Source of Truth**: Keycloak realm roles -- **Capabilities**: - - Access own profile - - Use platform features (based on group feature flags) - - Cannot manage other users - - Cannot modify feature flags - -## User Groups - -**Source of Truth**: All groups are managed in **Keycloak**. Jan Server reads group membership from JWT claims. - -### jan_group -- **Description**: Internal Jan team members -- **Membership Criteria**: Email domain `@jan.ai` OR `@menlo.ai` -- **Auto-assignment**: ✅ Automatic via Keycloak event listeners or registration flow -- **Keycloak Setup**: Create group in Keycloak realm -- **Default Feature Flags**: Experimental model access enabled - ```yaml - feature_flags: - - experimental_models # Can see all models including experimental ones - ``` - -### pilot_users -- **Description**: Beta testers and early adopters -- **Membership Criteria**: Manual assignment by admin -- **Auto-assignment**: ❌ Manual only -- **Keycloak Setup**: Create group in Keycloak realm -- **Default Feature Flags**: Experimental model access enabled - ```yaml - feature_flags: - - experimental_models # Can see experimental models for testing - ``` - -### standard -- **Description**: Verified regular users -- **Membership Criteria**: Verified email address -- **Auto-assignment**: ✅ Automatic after email verification -- **Keycloak Setup**: Create group in Keycloak realm -- **Default Feature Flags**: None (stable models only) - ```yaml - feature_flags: [] # Can only see stable/production models - ``` - -### guest -- **Description**: Guest/temporary access -- **Membership Criteria**: Guest login without full registration -- **Auto-assignment**: ✅ Automatic for guest login flow -- **Keycloak Setup**: Create group in Keycloak realm -- **Default Feature Flags**: None (stable models only) - ```yaml - feature_flags: [] # Can only see stable/production models - ``` - -## Feature Flags System - -### Overview -Feature flags allow fine-grained control over which features are available to users based on their group membership. - -**Current Implementation**: Single feature flag for experimental model access - -**Design Principles**: -- ✅ **Group-level only**: Feature flags defined per group -- ✅ **Additive inheritance**: Users get ALL flags from ALL their groups -- ✅ **Simple management**: No user-level overrides, no global flags -- ✅ **Keycloak-first**: Stored as Keycloak group attributes -- ✅ **JWT-based**: All feature flags included in JWT, no server-side caching or database lookups needed - -### Feature Flag Architecture - -**Source of Truth**: Keycloak group attributes - -**Resolution Logic**: -```go -// User has a feature if ANY of their groups has that feature -// All data comes from JWT claims - no database queries -func IsFeatureEnabled(c *gin.Context, flagKey string) bool { - claims := getJWTClaims(c) // Already validated by auth middleware - - // Defensive parsing of feature_flags array from JWT - featureFlagsRaw, ok := claims["feature_flags"] - if !ok { - return false - } - - // Handle different possible types - switch flags := featureFlagsRaw.(type) { - case []interface{}: - for _, flag := range flags { - if flagStr, ok := flag.(string); ok && flagStr == flagKey { - return true - } - } - case []string: - for _, flag := range flags { - if flag == flagKey { - return true - } - } - default: - // Unexpected type, log warning - log.Warn().Msgf("Unexpected feature_flags type: %T", flags) - } - - return false -} - -// Get user info from JWT with defensive parsing -func GetUserInfo(c *gin.Context) (UserInfo, error) { - claims := getJWTClaims(c) - - userInfo := UserInfo{} - - // Parse sub (required) - sub, ok := claims["sub"].(string) - if !ok { - return userInfo, fmt.Errorf("missing or invalid sub claim") - } - userInfo.ID = sub - - // Parse email (required) - email, ok := claims["email"].(string) - if !ok { - return userInfo, fmt.Errorf("missing or invalid email claim") - } - userInfo.Email = email - - // Parse name (optional) - if name, ok := claims["name"].(string); ok { - userInfo.Name = name - } - - // Parse email_verified (optional, defaults to false) - if emailVerified, ok := claims["email_verified"].(bool); ok { - userInfo.EmailVerified = emailVerified - } - - // Parse groups with type checking - if groupsRaw, ok := claims["groups"]; ok { - switch groups := groupsRaw.(type) { - case []interface{}: - for _, g := range groups { - if groupStr, ok := g.(string); ok { - userInfo.Groups = append(userInfo.Groups, groupStr) - } - } - case []string: - userInfo.Groups = groups - } - } - - // Check admin status - userInfo.IsAdmin = hasAdminRole(claims) - - return userInfo, nil -} -``` -``` - -### Feature Flag Storage - -#### In Keycloak (Source of Truth) -Store as group attributes: -```json -{ - "name": "jan_group", - "attributes": { - "feature_flags": ["experimental_models"] - } -} -``` - -#### In Jan Server Database (Definitions Only) -- `feature_flags` table - Feature flag metadata (key, name, description, category) -- `audit_logs` table - Audit trail for admin actions - -#### In JWT (All Runtime Data) -```json -{ - "sub": "user_id", - "email": "user@jan.ai", - "name": "John Doe", - "preferred_username": "john", - "email_verified": true, - "groups": ["/jan_group", "/pilot_users"], - "realm_access": { - "roles": ["admin"] - }, - "feature_flags": ["experimental_models"] -} -``` - -**JWT contains everything needed** - no database queries for user/group info - -### Available Feature Flags - -```yaml -# Model Access Control -experimental_models: Enable access to experimental/beta models in model_catalogs table - - Description: Users with this flag can see models marked as experimental=true - - Usage: Filter model catalog API based on this flag - - Groups with access: jan_group, pilot_users - - Default groups (standard, guest): No access to experimental models -``` - -### Model Catalog Integration - -Models in `model_catalogs` table have an `experimental` flag: - -```sql --- Example model catalog entries -INSERT INTO model_catalogs (id, model_id, name, experimental) VALUES - ('1', 'gpt-4', 'GPT-4', false), -- Stable, visible to all users - ('2', 'gpt-4-turbo', 'GPT-4 Turbo', true), -- Experimental, requires flag - ('3', 'claude-3', 'Claude 3', false), -- Stable, visible to all users - ('4', 'jan-v3-experimental', 'Jan v3', true); -- Experimental, requires flag -``` - -API filtering: -```go -func GetModelCatalog(c *gin.Context) { - userID := c.GetString("user_id") - hasExperimentalAccess := featureFlagService.IsFeatureEnabled(userID, "experimental_models") - - query := "SELECT * FROM model_catalogs WHERE 1=1" - if !hasExperimentalAccess { - query += " AND experimental = false" - } - query += " ORDER BY name" - - // Execute query... -} -``` - -## User Status - -### 🟢 Active -- User can log in and use the platform -- All features accessible based on role and group feature flags -- Default status for new users after verification -- **Keycloak Representation**: `enabled: true` - -### ⚪ Inactive -- User account disabled -- Cannot log in -- Sessions invalidated by Keycloak -- Reversible by admin -- **Keycloak Representation**: `enabled: false` - -### 🔵 Pending Verification -- User registered but email not verified -- Limited or no access until verification -- **Keycloak Representation**: `emailVerified: false` - -### 🔴 Banned -- User permanently restricted from access -- Cannot log in, requires admin intervention -- **Keycloak Representation**: `enabled: false` with custom attribute `banned: true` - -## API Security - Admin-Only Endpoints - -### Protected Routes with `RequireAdmin` Middleware - -All endpoints under `/api/v1/admin/**` require admin authentication: - -```go -adminRoutes := router.Group("/api/v1/admin") -adminRoutes.Use(middleware.RequireAdmin()) -{ - // User Management - adminRoutes.GET("/users", handlers.ListUsers) - adminRoutes.POST("/users", handlers.CreateUser) - adminRoutes.GET("/users/:id", handlers.GetUser) - adminRoutes.PATCH("/users/:id", handlers.UpdateUser) - adminRoutes.DELETE("/users/:id", handlers.DeleteUser) - adminRoutes.POST("/users/:id/activate", handlers.ActivateUser) - adminRoutes.POST("/users/:id/deactivate", handlers.DeactivateUser) - adminRoutes.POST("/users/:id/roles/:roleName", handlers.AssignRole) - adminRoutes.DELETE("/users/:id/roles/:roleName", handlers.RemoveRole) - - // Group Management - adminRoutes.GET("/groups", handlers.ListGroups) - adminRoutes.POST("/groups", handlers.CreateGroup) - adminRoutes.GET("/groups/:id", handlers.GetGroup) - adminRoutes.PATCH("/groups/:id", handlers.UpdateGroup) - adminRoutes.DELETE("/groups/:id", handlers.DeleteGroup) - adminRoutes.GET("/groups/:id/members", handlers.GetGroupMembers) - adminRoutes.POST("/users/:userId/groups/:groupId", handlers.AddUserToGroup) - adminRoutes.DELETE("/users/:userId/groups/:groupId", handlers.RemoveUserFromGroup) - - // Feature Flag Management (Group Level) - adminRoutes.GET("/feature-flags", handlers.ListFeatureFlags) - adminRoutes.POST("/feature-flags", handlers.CreateFeatureFlag) - adminRoutes.PATCH("/feature-flags/:id", handlers.UpdateFeatureFlag) - adminRoutes.DELETE("/feature-flags/:id", handlers.DeleteFeatureFlag) - adminRoutes.GET("/groups/:id/feature-flags", handlers.GetGroupFeatureFlags) - adminRoutes.PATCH("/groups/:id/feature-flags", handlers.SetGroupFeatureFlags) - adminRoutes.POST("/groups/:id/feature-flags/:flagKey", handlers.EnableGroupFeatureFlag) - adminRoutes.DELETE("/groups/:id/feature-flags/:flagKey", handlers.DisableGroupFeatureFlag) - - // System Monitoring - adminRoutes.GET("/system/health", handlers.GetSystemHealth) - adminRoutes.GET("/system/metrics", handlers.GetSystemMetrics) - adminRoutes.GET("/audit-logs", handlers.GetAuditLogs) -} -``` - -### RequireAdmin Middleware - -```go -// filepath: services/llm-api/internal/middleware/admin.go -package middleware - -import ( - "net/http" - "github.com/gin-gonic/gin" -) - -// Helper function to check admin status with defensive parsing -func isAdmin(claims map[string]interface{}) bool { - // Check realm_access.roles for 'admin' - realmAccessRaw, ok := claims["realm_access"] - if ok { - realmAccess, ok := realmAccessRaw.(map[string]interface{}) - if ok { - rolesRaw, ok := realmAccess["roles"] - if ok { - // Handle both []interface{} and []string - switch roles := rolesRaw.(type) { - case []interface{}: - for _, role := range roles { - if roleStr, ok := role.(string); ok && roleStr == "admin" { - return true - } - } - case []string: - for _, role := range roles { - if role == "admin" { - return true - } - } - } - } - } - } - - // Fallback: check is_admin attribute - if attributes, ok := claims["attributes"].(map[string]interface{}); ok { - if isAdminAttr, ok := attributes["is_admin"].(bool); ok && isAdminAttr { - return true - } - } - - return false -} - -func RequireAdmin() gin.HandlerFunc { - return func(c *gin.Context) { - claimsRaw, exists := c.Get("claims") - if !exists { - c.JSON(http.StatusUnauthorized, gin.H{ - "error": "Unauthorized", - "message": "Authentication required", - }) - c.Abort() - return - } - - claims, ok := claimsRaw.(map[string]interface{}) - if !ok { - c.JSON(http.StatusUnauthorized, gin.H{ - "error": "Unauthorized", - "message": "Invalid claims format", - }) - c.Abort() - return - } - - // Verify required claims exist - if _, ok := claims["sub"]; !ok { - c.JSON(http.StatusUnauthorized, gin.H{ - "error": "Unauthorized", - "message": "Missing user identifier", - }) - c.Abort() - return - } - - if _, ok := claims["email"]; !ok { - c.JSON(http.StatusUnauthorized, gin.H{ - "error": "Unauthorized", - "message": "Missing user email", - }) - c.Abort() - return - } - - // Check admin status using helper - if !isAdmin(claims) { - c.JSON(http.StatusForbidden, gin.H{ - "error": "Forbidden", - "message": "Admin access required", - }) - c.Abort() - return - } - - c.Next() - } -} -``` - -### Security Enhancements - -- [ ] **Audit Logging**: Log to PostgreSQL `audit_logs` table - - All admin actions with full context (user_id, email, action, resource, payload) - - Include IP address and user agent for security tracking - - Retention: 90 days (configurable) - - Indexed for fast querying by admin_user_id, resource_type, and created_at - -- [ ] **Input Validation**: Use `github.com/go-playground/validator/v10` - - Validate email format, name length, etc. - - Sanitize to prevent injection - - Validate flag keys (alphanumeric + underscore) - -- [ ] **CSRF Protection**: Use Gin CSRF middleware - - Token validation on all mutations - -- [ ] **MFA**: Enforce via Keycloak for admin users - -## Operational Guardrails - -### Rate Limiting -- [ ] **Implement rate limiting on admin endpoints** - - Use Kong rate limiting plugin or middleware - - Limit: 100 requests/minute per admin user - - Separate limits for read (higher) vs write (lower) operations - - Return 429 Too Many Requests with Retry-After header - -### Request Logging -- [ ] **Structured logging for all admin endpoints** - - Log level: INFO for successful operations, WARN for 4xx, ERROR for 5xx - - Include: timestamp, admin_user_id, admin_email, action, resource_type, resource_id, status_code, duration_ms - - Use correlation ID for request tracing across services - - Integrate with existing observability stack (Prometheus/Grafana) - -### Keycloak Resilience -- [ ] **Circuit breaker for Keycloak Admin API calls** - - Use `github.com/sony/gobreaker` (already in tech stack) - - Threshold: 5 consecutive failures - - Timeout: 30 seconds - - Reset after: 60 seconds of successful calls - - Fallback: Return 503 Service Unavailable with meaningful error - -- [ ] **Retry logic with exponential backoff** - - Max retries: 3 - - Initial delay: 100ms - - Max delay: 2 seconds - - Only retry on network errors or 5xx responses - - Do not retry on 4xx client errors - -- [ ] **Monitoring and alerting** - - Alert on circuit breaker open state - - Alert on Keycloak API latency > 1 second - - Alert on Keycloak API error rate > 5% - - Dashboard showing Keycloak health, request rate, error rate, latency percentiles - -## Implementation Tasks - -### Phase 0: Keycloak Integration - -- [ ] **Install Dependencies** - ```bash - go get github.com/Nerzal/gocloak/v13 - go get github.com/sony/gobreaker - go get github.com/go-playground/validator/v10 - ``` - -- [ ] **Keycloak Client Setup** - - `pkg/keycloak/client.go` - Admin client wrapper with connection pooling - - `pkg/keycloak/users.go` - User operations - - `pkg/keycloak/groups.go` - Group operations - - `pkg/keycloak/roles.go` - Role operations -### Phase 1: Database Schema (Minimal) - -- [ ] **Create Migration**: `services/llm-api/migrations/YYYYMMDD_user_management.sql` - -- [ ] **feature_flags** table (definitions only): - ```sql - CREATE TABLE feature_flags ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - key VARCHAR(50) UNIQUE NOT NULL, - name VARCHAR(255) NOT NULL, - description TEXT, - category VARCHAR(50), - metadata JSONB, - created_at TIMESTAMP DEFAULT NOW(), - updated_at TIMESTAMP DEFAULT NOW() - ); - CREATE INDEX idx_feature_flags_key ON feature_flags(key); - CREATE INDEX idx_feature_flags_category ON feature_flags(category); - - -- Insert the experimental_models flag - INSERT INTO feature_flags (key, name, description, category) VALUES - ('experimental_models', 'Experimental Models', 'Access to experimental/beta models in model catalog', 'model_access'); - ``` - -- [ ] **audit_logs** table: - ```sql - CREATE TABLE audit_logs ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - admin_user_id VARCHAR(255) NOT NULL, -- From JWT sub - admin_email VARCHAR(255) NOT NULL, -- From JWT email - action VARCHAR(100) NOT NULL, - resource_type VARCHAR(50) NOT NULL, - resource_id VARCHAR(255), - payload JSONB, - ip_address VARCHAR(45), - user_agent TEXT, - status_code INTEGER, - error_message TEXT, - created_at TIMESTAMP DEFAULT NOW() - ); - CREATE INDEX idx_audit_logs_created_at ON audit_logs(created_at DESC); - CREATE INDEX idx_audit_logs_admin_user_id ON audit_logs(admin_user_id); - CREATE INDEX idx_audit_logs_resource_type ON audit_logs(resource_type); - ``` - -- [ ] **Add experimental column to model_catalogs**: - ```sql - ALTER TABLE model_catalogs ADD COLUMN IF NOT EXISTS experimental BOOLEAN DEFAULT FALSE; - CREATE INDEX idx_model_catalogs_experimental ON model_catalogs(experimental); - ``` - -### Phase 2: JWT & Authentication - -- [ ] **JWT Middleware Enhancement**: `services/llm-api/internal/middleware/auth.go` - ```go - // Extract and store JWT claims in context - func JWTAuth() gin.HandlerFunc { - return func(c *gin.Context) { - // Validate JWT (already done by Kong or existing middleware) - claims := extractJWTClaims(c) - - // Store in context for easy access - c.Set("claims", claims) - c.Set("user_id", claims["sub"]) - c.Set("user_email", claims["email"]) - c.Set("user_groups", claims["groups"]) - c.Set("feature_flags", claims["feature_flags"]) - - c.Next() - } - } - ``` - -- [ ] **RequireAdmin Middleware**: Check JWT for admin role - ```go - func RequireAdmin() gin.HandlerFunc { - return func(c *gin.Context) { - claims := c.MustGet("claims").(map[string]interface{}) - - // Check admin status using helper function - if !isAdmin(claims) { - c.JSON(http.StatusForbidden, gin.H{ - "error": "Forbidden", - "message": "Admin access required", - }) - c.Abort() - return - } - - c.Next() - } - } - ``` - -### Phase 3: Model Catalog Filtering - -- [ ] **Update Model Catalog Handler**: `services/llm-api/internal/handlers/model_catalog.go` - ```go - func GetModelCatalog(c *gin.Context) { - // Check if user has experimental model access from JWT - hasExperimentalAccess := IsFeatureEnabled(c, "experimental_models") - - query := "SELECT * FROM model_catalogs WHERE 1=1" - if !hasExperimentalAccess { - query += " AND (experimental = false OR experimental IS NULL)" - } - query += " ORDER BY name" - - // Execute query and return models - rows, err := db.Query(query) - if err != nil { - c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch models"}) - return - } - defer rows.Close() - - // Parse and return results... - } - ``` - ``` - -### Phase 4: User Management API - -- [ ] **Implement User Management Handlers**: `services/llm-api/internal/handlers/admin/users.go` - - ListUsers, CreateUser, GetUser, UpdateUser, DeleteUser - - ActivateUser, DeactivateUser - - All operations via Keycloak Admin API - - Audit logging for all actions - -### Phase 5: Group Management API - -- [ ] **Implement Group Management Handlers**: `services/llm-api/internal/handlers/admin/groups.go` - - ListGroups, CreateGroup, GetGroup, UpdateGroup, DeleteGroup - - GetGroupMembers, AddUserToGroup, RemoveUserFromGroup - - All operations via Keycloak Admin API - - Audit logging for all actions - -### Phase 6: Feature Flag Management API - -- [ ] **Implement Feature Flag Handlers**: `services/llm-api/internal/handlers/admin/feature_flags.go` - - ListFeatureFlags (from database), CreateFeatureFlag, UpdateFeatureFlag, DeleteFeatureFlag - - GetGroupFeatureFlags (from Keycloak), SetGroupFeatureFlags - - EnableGroupFeatureFlag, DisableGroupFeatureFlag - - Update Keycloak group attributes - - Audit logging for all flag changes - -### Phase 7: Keycloak JWT Mapper Configuration - -- [ ] **Configure Group Mapper** in Keycloak to include group paths in JWT - - Mapper Type: **Group Membership** - - Token Claim Name: `groups` - - Full group path: **Yes** (includes leading slash: `/jan_group`, `/pilot_users`) - - Add to ID token: Yes - - Add to access token: Yes - - Add to userinfo: Yes - -- [ ] **Configure Group Attribute Mapper** for feature flags - - Mapper Type: **Group Attribute Mapper** (or **User Attribute** if aggregating) - - Group Attribute: `feature_flags` - - Token Claim Name: `feature_flags` - - Claim JSON Type: JSON (array of strings) - - Aggregate attribute values: **Yes** (combine from all groups) - - Add to ID token: Yes - - Add to access token: Yes - -- [ ] **Note on Group Paths**: - - Keycloak includes leading slash in group paths: `/jan_group`, `/standard` - - Nested groups use hierarchy: `/parent/child` - - Strip leading slash in application code if needed for comparisons - - Group inheritance: Users in child groups automatically in parent groups - -- [ ] **Example JWT with all needed data**: - ```json - { - "sub": "user-uuid", - "email": "user@jan.ai", - "name": "John Doe", - "preferred_username": "john", - "email_verified": true, - "groups": ["/jan_group", "/standard"], - "realm_access": { - "roles": ["admin", "user"] - }, - "feature_flags": ["experimental_models"] - } - ``` - -### Phase 8: Testing & Validation - -- [ ] **Unit Tests**: Test feature flag resolution, admin middleware, JWT parsing -- [ ] **Integration Tests**: Test model catalog filtering with different user groups -- [ ] **End-to-End Tests**: Test admin operations with Keycloak -- [ ] **Performance Tests**: Validate JWT-only approach performance -## Database Queries - -### Get Users with Experimental Model Access (from audit logs) -```sql --- This is for audit/reporting only, not for runtime checks -SELECT DISTINCT admin_email, action, created_at -FROM audit_logs -WHERE resource_type = 'group_feature_flag' -AND payload->>'flag_key' = 'experimental_models' -ORDER BY created_at DESC; -``` - -### Get User's Effective Feature Flags (from JWT) -```go -// No database query needed - read from JWT claims -func GetUserFeatures(c *gin.Context) []string { - if featureFlags, exists := c.Get("feature_flags"); exists { - if flags, ok := featureFlags.([]string); ok { - return flags - } - } - return []string{} -} -``` - -### Get Experimental Models -```sql --- Get all experimental models (admin view) -SELECT * FROM model_catalogs -WHERE experimental = true -ORDER BY name; - --- Get models available to user without experimental access -SELECT * FROM model_catalogs -WHERE experimental = false OR experimental IS NULL -ORDER BY name; - --- Get all models (for users with experimental_models flag) -SELECT * FROM model_catalogs -ORDER BY name; -``` - -### Feature Flag Audit Trail -```sql --- See who changed feature flags for a group -SELECT - admin_email, - action, - payload->>'group_id' as group_id, - payload->>'group_name' as group_name, - payload->>'flag_key' as flag_key, - payload->>'enabled' as enabled, - created_at -FROM audit_logs -WHERE resource_type = 'group_feature_flag' -ORDER BY created_at DESC -LIMIT 100; -``` -## Future Enhancements - -- [ ] **User impersonation** (via Keycloak Admin API) - - Allow admins to impersonate users for troubleshooting - - Audit log all impersonation sessions - -- [ ] **Bulk operations** - - Bulk user import/export - - Bulk group membership changes - - Bulk feature flag assignments - -- [ ] **Additional feature flags** as product evolves - - `fine_tuning`: Access to model fine-tuning features - - `custom_models`: Upload and use custom models - - `advanced_analytics`: Access to advanced usage analytics - - `api_access`: Programmatic API access with keys - -- [ ] **Advanced feature flag capabilities** - - A/B testing with user percentage rollout - - Gradual rollout with canary deployment - - Time-based scheduling (enable/disable at specific times) - - Environment-specific flags (dev/staging/prod) - -## Frontend Implementation Scope (Future) - -**Note**: Frontend implementation is out of scope for this backend-focused document. If a UI is needed, it should: - -- [ ] **Admin Dashboard** (separate project) - - User management interface (list, create, edit, deactivate users) - - Group management interface (list, create, edit, manage members) - - Feature flag management UI (assign flags to groups) - - Audit log viewer with filtering and export - -- [ ] **Tech Stack Recommendations** - - Framework: React or Next.js - - UI Library: shadcn/ui or Material-UI - - State Management: React Query (for API cache management) - - Tables: TanStack Table - - Forms: React Hook Form + Zod validation - -- [ ] **Authentication** - - Use Keycloak's JavaScript adapter for SSO - - Redirect to Keycloak login - - Store JWT in httpOnly cookie or memory - - Handle token refresh automatically - -**For now, admin operations can be performed via:** -- Direct Keycloak Admin Console -- API testing tools (Postman, Insomnia, curl) -- Custom CLI tools if needed - -## Architecture Summary - -### Key Principles -- **JWT-Only**: All user/group/feature flag data comes from JWT claims - no server-side cache or sync -- **Keycloak as Source of Truth**: Users, groups, roles, and feature flag assignments managed in Keycloak -- **Minimal Database**: Only audit logs and feature flag definitions (metadata) stored in PostgreSQL -- **Real-Time Admin Operations**: Admin API calls directly to Keycloak for immediate effect -- **Automatic JWT Updates**: Feature flag changes reflected in next JWT after token refresh (handled by Keycloak) \ No newline at end of file diff --git a/apps/platform/content/docs/guides/user-settings-personalization.mdx b/apps/platform/content/docs/guides/user-settings-personalization.mdx deleted file mode 100644 index 21c1f775..00000000 --- a/apps/platform/content/docs/guides/user-settings-personalization.mdx +++ /dev/null @@ -1,185 +0,0 @@ ---- -title: "User Settings & Personalization Guide" ---- - -# User Settings & Personalization Guide - -Complete guide for personalizing Jan Server with user settings and preferences. - -## Overview - -The User Settings API allows users to customize their Jan Server experience including: - -- **Memory Configuration**: Control how conversations are remembered and injected into responses -- **Profile Settings**: Define user identity (name, role, style preferences) -- **Advanced Features**: Toggle web search, code execution, and tool access -- **Preferences**: Additional configuration options for personalization - -User settings directly influence conversation behavior and response generation. - -## Quick Start - -### Get Your Settings - -```bash -# Retrieve your current settings -curl -H "Authorization: Bearer " \ - http://localhost:8000/v1/users/me/settings -``` - -### Update Your Settings - -```bash -# Personalize your profile -curl -X PATCH http://localhost:8000/v1/users/me/settings \ - -H "Authorization: Bearer " \ - -H "Content-Type: application/json" \ - -d '{ - "profile_settings": { - "base_style": "Professional", - "nick_name": "Alex", - "occupation": "Software Engineer", - "custom_instructions": "Always provide code examples" - } - }' -``` - -## Settings Structure - -### Profile Settings - -Control how the AI perceives and responds to you. - -| Setting | Type | Options | Default | Description | -|---------|------|---------|---------|-------------| -| `base_style` | Enum | `Concise`, `Friendly`, `Professional` | `Friendly` | Tone and style of responses | -| `nick_name` | String | Any (255 chars max) | Empty | Your preferred name/alias | -| `occupation` | String | Any (255 chars max) | Empty | Your role or profession | -| `custom_instructions` | String | Any | Empty | Instructions injected into every conversation | -| `more_about_you` | String | Any | Empty | Additional context about yourself | - -#### Example Profile Configurations - -**Software Engineer:** -```json -{ - "profile_settings": { - "base_style": "Professional", - "nick_name": "Dev", - "occupation": "Senior Software Engineer", - "custom_instructions": "Provide code examples in Python, Go, and TypeScript. Explain architectural decisions.", - "more_about_you": "5 years experience in backend systems. Interested in performance optimization." - } -} -``` - -**Student:** -```json -{ - "profile_settings": { - "base_style": "Friendly", - "nick_name": "Jamie", - "occupation": "Computer Science Student", - "custom_instructions": "Explain concepts step-by-step. Include learning resources.", - "more_about_you": "Currently learning machine learning and data science." - } -} -``` - -**Content Creator:** -```json -{ - "profile_settings": { - "base_style": "Creative", - "nick_name": "Creator", - "occupation": "Technical Content Creator", - "custom_instructions": "Provide engaging explanations suitable for blog posts. Include examples.", - "more_about_you": "Writing about AI, machine learning, and cloud technologies." - } -} -``` - -### Memory Configuration - -Control how the system remembers conversations and uses memory in future interactions. - -| Setting | Type | Default | Range | Description | -|---------|------|---------|-------|-------------| -| `enabled` | Boolean | `true` | N/A | Master toggle for memory system | -| `observe_enabled` | Boolean | `true` | N/A | Observe and learn from conversations | -| `inject_user_core` | Boolean | `true` | N/A | Inject user profile into responses | -| `inject_semantic` | Boolean | `true` | N/A | Use semantic memory (topic-based) | -| `inject_episodic` | Boolean | `false` | N/A | Use episodic memory (conversation history) | -| `max_user_items` | Integer | 3 | 1-10 | Max user memory items to inject | -| `max_project_items` | Integer | 5 | 1-20 | Max project memory items to inject | -| `max_episodic_items` | Integer | 3 | 1-10 | Max conversation history items to inject | -| `min_similarity` | Float | 0.75 | 0.0-1.0 | Similarity threshold for memory injection | - -### Advanced Settings - -Toggle advanced features and capabilities. - -| Setting | Type | Default | Description | -|---------|------|---------|-------------| -| `web_search` | Boolean | `false` | Allow agents to perform web searches | -| `code_enabled` | Boolean | `false` | Allow agents to execute code | - -## API Reference - -### Get User Settings - -**GET** `/v1/users/me/settings` - -Retrieve authenticated user's settings. See [User Settings API](../api/llm-api/README#user-settings) for complete reference. - -### Update User Settings - -**PATCH** `/v1/users/me/settings` - -Update any combination of settings (partial update supported). - -## Usage Examples - -### JavaScript - -```javascript -const token = 'your_token_here'; -const headers = { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' -}; - -const getResponse = await fetch('http://localhost:8000/v1/users/me/settings', { - method: 'GET', - headers -}); - -const settings = await getResponse.json(); -settings.profile_settings.base_style = 'Professional'; - -await fetch('http://localhost:8000/v1/users/me/settings', { - method: 'PATCH', - headers, - body: JSON.stringify(settings) -}); -``` - -## Best Practices - -1. **Complete Profile**: Set `nick_name` and `occupation` so the AI knows who you are -2. **Custom Instructions**: Add preferences once; they apply to all conversations -3. **Memory Settings**: Start with defaults, increase if you want richer context -4. **Web Search**: Enable only if agents need to browse current information -5. **Code Execution**: Enable only if your use case requires running code - -## Related Documentation - -- [User Settings API Reference](../api/llm-api/README#user-settings) - Complete API reference -- [Conversation Management](./conversation-management) - Managing your conversations -- [LLM API Documentation](../api/llm-api/) - Main API reference -- [Authentication Guide](./authentication) - Getting tokens and keys - ---- - -**Last Updated**: December 23, 2025 -**Compatibility**: Jan Server v0.0.14+ diff --git a/apps/platform/content/docs/guides/webhooks.mdx b/apps/platform/content/docs/guides/webhooks.mdx deleted file mode 100644 index 0ac2c217..00000000 --- a/apps/platform/content/docs/guides/webhooks.mdx +++ /dev/null @@ -1,864 +0,0 @@ ---- -title: "Webhooks & Event Integration Guide" ---- - -# Webhooks & Event Integration Guide - -> **Status:** v0.0.14 | **Last Updated:** December 23, 2025 - -Webhooks enable real-time event notifications from Jan Server to your systems. This guide covers setting up, securing, and handling webhook events for conversations, messages, models, and MCP tools. - -## Table of Contents - -- [Quick Start](#quick-start) -- [Event Types](#event-types) -- [Webhook Setup](#webhook-setup) -- [Payload Structure](#payload-structure) -- [Retry & Failure Handling](#retry--failure-handling) -- [Security & Verification](#security--verification) -- [Real-World Use Cases](#real-world-use-cases) -- [Testing Webhooks](#testing-webhooks) -- [Best Practices](#best-practices) - ---- - -## Quick Start - -### 1. Create Webhook Endpoint - -```python -# webhook_receiver.py -from fastapi import FastAPI, Request, HTTPException -import hmac -import hashlib -import json -from datetime import datetime - -app = FastAPI() - -WEBHOOK_SECRET = "your-webhook-secret" - -@app.post("/webhooks/jan-server") -async def handle_webhook(request: Request): - """Handle webhooks from Jan Server""" - - # 1. Verify signature - signature = request.headers.get("X-Signature") - timestamp = request.headers.get("X-Timestamp") - - body = await request.body() - - if not verify_signature(body, signature, timestamp): - raise HTTPException(status_code=401, detail="Invalid signature") - - # 2. Parse event - event = json.loads(body) - - # 3. Handle event - if event["type"] == "conversation.created": - await handle_conversation_created(event) - elif event["type"] == "message.sent": - await handle_message_sent(event) - elif event["type"] == "model.updated": - await handle_model_updated(event) - - return {"status": "ok"} - -def verify_signature(body: bytes, signature: str, timestamp: str) -> bool: - """Verify webhook signature""" - message = f"{timestamp}.{body.decode()}" - expected = hmac.new( - WEBHOOK_SECRET.encode(), - message.encode(), - hashlib.sha256 - ).hexdigest() - return hmac.compare_digest(signature, expected) -``` - -### 2. Register Webhook - -```bash -curl -X POST http://localhost:8000/v1/admin/webhooks \ - -H "Authorization: Bearer admin-token" \ - -H "Content-Type: application/json" \ - -d '{ - "url": "https://your-domain.com/webhooks/jan-server", - "events": ["conversation.*", "message.sent"], - "active": true, - "secret": "your-webhook-secret" - }' -``` - -### 3. Start Receiving Events - -Events will now be POST'd to your webhook URL in real-time! - ---- - -## Event Types - -### Conversation Events - -#### conversation.created -Fired when a new conversation is created. - -```json -{ - "type": "conversation.created", - "id": "evt_abc123", - "timestamp": "2025-12-23T10:30:00Z", - "data": { - "conversation_id": "conv_123", - "user_id": "user_456", - "title": "New Conversation", - "created_at": "2025-12-23T10:30:00Z", - "metadata": { - "source": "web", - "ip": "192.168.1.1" - } - } -} -``` - -#### conversation.updated -Fired when conversation is modified (title, metadata, etc). - -```json -{ - "type": "conversation.updated", - "id": "evt_def456", - "timestamp": "2025-12-23T10:35:00Z", - "data": { - "conversation_id": "conv_123", - "changes": { - "title": { - "old": "Old Title", - "new": "New Title" - } - }, - "updated_at": "2025-12-23T10:35:00Z" - } -} -``` - -#### conversation.deleted -Fired when conversation is deleted. - -```json -{ - "type": "conversation.deleted", - "id": "evt_ghi789", - "timestamp": "2025-12-23T10:40:00Z", - "data": { - "conversation_id": "conv_123", - "user_id": "user_456", - "message_count": 15, - "deleted_at": "2025-12-23T10:40:00Z" - } -} -``` - -### Message Events - -#### message.sent -Fired when a new message is sent in conversation. - -```json -{ - "type": "message.sent", - "id": "evt_jkl012", - "timestamp": "2025-12-23T10:45:00Z", - "data": { - "conversation_id": "conv_123", - "message_id": "msg_789", - "role": "assistant", - "content": "Response text...", - "model": "gpt-4", - "tokens_used": { - "prompt": 150, - "completion": 280 - }, - "sent_at": "2025-12-23T10:45:00Z" - } -} -``` - -#### message.edited -Fired when message is edited/regenerated. - -```json -{ - "type": "message.edited", - "id": "evt_mno345", - "timestamp": "2025-12-23T10:50:00Z", - "data": { - "conversation_id": "conv_123", - "message_id": "msg_789", - "previous_content": "Old content...", - "new_content": "New content...", - "edited_at": "2025-12-23T10:50:00Z" - } -} -``` - -#### message.deleted -Fired when message is deleted. - -```json -{ - "type": "message.deleted", - "id": "evt_pqr678", - "timestamp": "2025-12-23T10:55:00Z", - "data": { - "conversation_id": "conv_123", - "message_id": "msg_789", - "role": "assistant", - "deleted_at": "2025-12-23T10:55:00Z" - } -} -``` - -### Model & Tool Events - -#### model.added -Fired when new model/provider added to catalog. - -```json -{ - "type": "model.added", - "id": "evt_stu901", - "timestamp": "2025-12-23T11:00:00Z", - "data": { - "model_id": "gpt-4-turbo", - "provider": "openai", - "capabilities": ["chat", "vision", "function_calling"], - "added_at": "2025-12-23T11:00:00Z" - } -} -``` - -#### model.updated -Fired when model configuration changes. - -```json -{ - "type": "model.updated", - "id": "evt_vwx234", - "timestamp": "2025-12-23T11:05:00Z", - "data": { - "model_id": "gpt-4-turbo", - "changes": { - "available": { - "old": true, - "new": false - } - }, - "updated_at": "2025-12-23T11:05:00Z" - } -} -``` - -#### mcp_tool.enabled -Fired when MCP tool is enabled. - -```json -{ - "type": "mcp_tool.enabled", - "id": "evt_yza567", - "timestamp": "2025-12-23T11:10:00Z", - "data": { - "tool_id": "web_scraper", - "tool_name": "Web Scraper", - "enabled_at": "2025-12-23T11:10:00Z", - "enabled_by": "admin_user_123" - } -} -``` - -#### mcp_tool.disabled -Fired when MCP tool is disabled. - -```json -{ - "type": "mcp_tool.disabled", - "id": "evt_bcd890", - "timestamp": "2025-12-23T11:15:00Z", - "data": { - "tool_id": "web_scraper", - "tool_name": "Web Scraper", - "disabled_at": "2025-12-23T11:15:00Z", - "disabled_by": "admin_user_123", - "reason": "Content filtering rule violation" - } -} -``` - ---- - -## Webhook Setup - -### Register a Webhook - -```bash -curl -X POST http://localhost:8000/v1/admin/webhooks \ - -H "Authorization: Bearer your-admin-token" \ - -H "Content-Type: application/json" \ - -d '{ - "url": "https://your-domain.com/webhooks/jan-server", - "events": [ - "conversation.*", - "message.sent", - "message.edited", - "model.*", - "mcp_tool.*" - ], - "active": true, - "secret": "your-webhook-secret-key", - "headers": { - "X-Custom-Header": "custom-value" - } - }' -``` - -### List Webhooks - -```bash -curl -X GET http://localhost:8000/v1/admin/webhooks \ - -H "Authorization: Bearer your-admin-token" -``` - -### Update Webhook - -```bash -curl -X PATCH http://localhost:8000/v1/admin/webhooks/webhook_123 \ - -H "Authorization: Bearer your-admin-token" \ - -H "Content-Type: application/json" \ - -d '{ - "events": ["conversation.*"], - "active": false - }' -``` - -### Delete Webhook - -```bash -curl -X DELETE http://localhost:8000/v1/admin/webhooks/webhook_123 \ - -H "Authorization: Bearer your-admin-token" -``` - -### Event Wildcards - -Use wildcards to subscribe to event families: - -```json -{ - "events": [ - "conversation.*", // All conversation events - "message.*", // All message events - "model.*", // All model events - "mcp_tool.*", // All MCP tool events - "*" // All events - ] -} -``` - ---- - -## Payload Structure - -### Standard Event Envelope - -```json -{ - "type": "event.type", - "id": "evt_unique_id", - "timestamp": "2025-12-23T11:30:00Z", - "webhook_id": "webhook_123", - "retry_count": 0, - "data": { - // Event-specific data - } -} -``` - -### Headers Sent - -``` -POST /webhooks/endpoint HTTP/1.1 -Host: your-domain.com -Content-Type: application/json -Content-Length: 1234 - -X-Signature: sha256=abcdef... // HMAC-SHA256 signature -X-Timestamp: 1703330400 // Unix timestamp -X-Event-Type: conversation.created -X-Event-ID: evt_unique_id -X-Webhook-ID: webhook_123 -``` - ---- - -## Retry & Failure Handling - -### Automatic Retries - -Jan Server automatically retries failed deliveries with exponential backoff: - -``` -Attempt 1: Immediately -Attempt 2: 5 seconds later -Attempt 3: 25 seconds later (5 * 5) -Attempt 4: 2 minutes later -Attempt 5: 10 minutes later -Attempt 6: 50 minutes later -Attempt 7: 4 hours later -Attempt 8: 24 hours later -``` - -Webhooks are retried for: -- `5xx` server errors -- Connection timeouts -- Network errors - -**Not** retried for: -- `4xx` client errors (except timeout) -- Successful delivery (2xx response) - -### Idempotent Processing - -Handle duplicate deliveries by checking `X-Event-ID`: - -```python -# Store processed event IDs in database -processed_events = set() - -@app.post("/webhooks/jan-server") -async def handle_webhook(request: Request): - event_id = request.headers.get("X-Event-ID") - - # Skip if already processed - if event_id in processed_events: - return {"status": "ok", "cached": True} - - # Process event - await process_event(await request.json()) - - # Mark as processed - processed_events.add(event_id) - - return {"status": "ok"} -``` - -### Webhook Response Codes - -Return appropriate HTTP status codes: - -```python -@app.post("/webhooks/jan-server") -async def handle_webhook(request: Request): - try: - event = await request.json() - - # Process event - await process_event(event) - - # Return 200-299 for success - return {"status": "ok"}, 200 - - except ValueError: - # 4xx errors are not retried - return {"error": "Invalid JSON"}, 400 - - except Exception as e: - # 5xx errors trigger retry - logger.error(f"Webhook processing failed: {e}") - return {"error": "Internal error"}, 500 -``` - ---- - -## Security & Verification - -### Signature Verification (HMAC-SHA256) - -```python -import hmac -import hashlib -import json - -def verify_webhook_signature( - body: bytes, - signature: str, - timestamp: str, - secret: str, - max_age: int = 300 # 5 minutes -) -> bool: - """ - Verify webhook signature - - Args: - body: Raw request body bytes - signature: X-Signature header value - timestamp: X-Timestamp header value - secret: Your webhook secret - max_age: Max age of timestamp in seconds - - Returns: - True if signature is valid - """ - - # 1. Check timestamp freshness (prevent replay attacks) - import time - event_time = int(timestamp) - current_time = int(time.time()) - - if abs(current_time - event_time) > max_age: - return False - - # 2. Compute expected signature - message = f"{timestamp}.{body.decode()}" - expected_sig = hmac.new( - secret.encode(), - message.encode(), - hashlib.sha256 - ).hexdigest() - - # 3. Compare using constant-time comparison - return hmac.compare_digest(signature, expected_sig) -``` - -### Secure Webhook Endpoint - -```python -from fastapi import FastAPI, Request, HTTPException -import logging - -app = FastAPI() -logger = logging.getLogger(__name__) - -WEBHOOK_SECRET = "your-webhook-secret" -MAX_BODY_SIZE = 1_000_000 # 1MB - -@app.post("/webhooks/jan-server") -async def handle_webhook(request: Request): - """Secure webhook endpoint""" - - # 1. Check content type - if request.headers.get("content-type") != "application/json": - raise HTTPException(status_code=400, detail="Invalid content type") - - # 2. Read body (with size limit) - body = await request.body() - if len(body) > MAX_BODY_SIZE: - raise HTTPException(status_code=413, detail="Payload too large") - - # 3. Verify signature - signature = request.headers.get("X-Signature") - timestamp = request.headers.get("X-Timestamp") - - if not signature or not timestamp: - logger.warning("Missing signature or timestamp headers") - raise HTTPException(status_code=401, detail="Missing headers") - - if not verify_webhook_signature(body, signature, timestamp, WEBHOOK_SECRET): - logger.warning(f"Invalid signature from {request.client.host}") - raise HTTPException(status_code=401, detail="Invalid signature") - - # 4. Parse and process - try: - event = json.loads(body) - await process_event(event) - except json.JSONDecodeError: - raise HTTPException(status_code=400, detail="Invalid JSON") - except Exception as e: - logger.error(f"Webhook processing error: {e}", exc_info=True) - raise HTTPException(status_code=500, detail="Processing error") - - return {"status": "ok"} -``` - ---- - -## Real-World Use Cases - -### Use Case 1: Conversation Notification System - -```python -# Send notifications when conversations are created -import aiohttp - -@app.post("/webhooks/jan-server") -async def handle_webhook(request: Request): - event = await request.json() - - if event["type"] == "conversation.created": - conversation = event["data"] - user_id = conversation["user_id"] - - # Send notification to user - await send_notification( - user_id=user_id, - title="New Conversation", - body=f"Conversation created: {conversation['title']}", - data={"conversation_id": conversation["conversation_id"]} - ) - - return {"status": "ok"} - -async def send_notification(user_id: str, title: str, body: str, data: dict): - """Send push notification (Firebase example)""" - async with aiohttp.ClientSession() as session: - await session.post( - "https://fcm.googleapis.com/fcm/send", - json={ - "to": f"/topics/user_{user_id}", - "notification": {"title": title, "body": body}, - "data": data - }, - headers={"Authorization": f"key={FCM_KEY}"} - ) -``` - -### Use Case 2: Model Catalog Sync - -```python -# Sync model updates to external system -@app.post("/webhooks/jan-server") -async def handle_webhook(request: Request): - event = await request.json() - - if event["type"] == "model.updated": - model_data = event["data"] - - # Update external system - await sync_model_to_external_db( - model_id=model_data["model_id"], - changes=model_data["changes"] - ) - - # Invalidate cache - await cache.delete(f"model_{model_data['model_id']}") - - return {"status": "ok"} - -async def sync_model_to_external_db(model_id: str, changes: dict): - """Sync to PostgreSQL""" - async with db.pool.acquire() as conn: - for field, change in changes.items(): - await conn.execute( - "UPDATE models SET $1 = $2 WHERE model_id = $3", - field, - change["new"], - model_id - ) -``` - -### Use Case 3: Audit Logging - -```python -from datetime import datetime - -@app.post("/webhooks/jan-server") -async def handle_webhook(request: Request): - event = await request.json() - - # Log all events to audit database - await log_audit_event( - event_type=event["type"], - event_id=event["id"], - timestamp=event["timestamp"], - data=event["data"], - received_at=datetime.now() - ) - - return {"status": "ok"} - -async def log_audit_event(event_type: str, event_id: str, timestamp: str, data: dict, received_at: datetime): - """Store audit log""" - async with db.pool.acquire() as conn: - await conn.execute( - """ - INSERT INTO audit_log (event_type, event_id, timestamp, data, received_at) - VALUES ($1, $2, $3, $4, $5) - """, - event_type, event_id, timestamp, json.dumps(data), received_at - ) -``` - ---- - -## Testing Webhooks - -### Local Testing with ngrok - -```bash -# 1. Start webhook server locally -python webhook_server.py - -# 2. Expose with ngrok -ngrok http 8000 - -# 3. Copy ngrok URL (e.g., https://abc-def-ghi.ngrok.io) - -# 4. Register webhook pointing to ngrok URL -curl -X POST http://localhost:8000/v1/admin/webhooks \ - -H "Authorization: Bearer token" \ - -d '{ - "url": "https://abc-def-ghi.ngrok.io/webhooks/jan-server", - "events": ["conversation.*"], - "secret": "test-secret" - }' - -# 5. Trigger events (create conversation, etc) -# 6. See logs in terminal: "Received webhook: {...}" -``` - -### Mock Webhook Testing - -```python -# test_webhooks.py -import pytest -import json -import hmac -import hashlib -import time - -@pytest.mark.asyncio -async def test_conversation_created_webhook(client): - """Test conversation.created webhook""" - - # Prepare webhook payload - event = { - "type": "conversation.created", - "id": "evt_test_123", - "timestamp": "2025-12-23T11:30:00Z", - "data": { - "conversation_id": "conv_test", - "user_id": "user_test", - "title": "Test Conversation" - } - } - - body = json.dumps(event).encode() - timestamp = str(int(time.time())) - - # Compute signature - message = f"{timestamp}.{body.decode()}" - signature = hmac.new( - b"test-secret", - message.encode(), - hashlib.sha256 - ).hexdigest() - - # Send webhook - response = await client.post( - "/webhooks/jan-server", - json=event, - headers={ - "X-Signature": signature, - "X-Timestamp": timestamp, - "X-Event-ID": event["id"] - } - ) - - assert response.status_code == 200 - - # Verify event was processed - processed_event = await db.fetch_one( - "SELECT * FROM processed_events WHERE event_id = $1", - event["id"] - ) - assert processed_event is not None -``` - ---- - -## Best Practices - -### 1. Always Verify Signatures - -```python -# ✅ DO THIS -if not verify_webhook_signature(body, signature, timestamp, secret): - raise HTTPException(status_code=401) - -# ❌ DON'T DO THIS -event = json.loads(body) # Without verification! -``` - -### 2. Return Quickly (< 5 seconds) - -```python -# ✅ DO THIS - Queue for async processing -@app.post("/webhooks/jan-server") -async def handle_webhook(request: Request): - event = await request.json() - await task_queue.enqueue(process_event, event) # Non-blocking - return {"status": "ok"} - -# ❌ DON'T DO THIS - Blocking operations -@app.post("/webhooks/jan-server") -async def handle_webhook(request: Request): - event = await request.json() - await process_event_slowly(event) # 10+ seconds! - return {"status": "ok"} -``` - -### 3. Log Everything - -```python -logger.info( - f"Webhook received", - extra={ - "event_type": event["type"], - "event_id": event["id"], - "timestamp": event["timestamp"], - "webhook_id": request.headers.get("X-Webhook-ID") - } -) -``` - -### 4. Handle Duplicates - -```python -# Use event ID for deduplication -event_id = event["id"] -if await cache.get(f"webhook:{event_id}"): - return {"status": "ok"} # Already processed - -# Process... - -await cache.set(f"webhook:{event_id}", True, ex=86400) # 24hr TTL -``` - -### 5. Monitor Webhook Health - -```python -# Track delivery success rate -metrics.webhook_deliveries_total.labels( - event_type=event["type"], - status="success" -).inc() - -# Alert on failures -if response_status >= 500: - alert.send( - "Webhook delivery failed", - f"Event {event['id']} failed after retries" - ) -``` - ---- - -## Webhook Delivery Guarantees - -| Guarantee | Behavior | -|-----------|----------| -| **At-least-once** | Events delivered at least once; check event ID for duplicates | -| **Order not guaranteed** | Events may arrive out of order; use timestamps | -| **No guaranteed latency** | Typically < 5 seconds; retry delays can extend this | -| **30-day retention** | Event logs available for 30 days in admin API | - ---- - -See [Monitoring & Troubleshooting Guide](./monitoring-advanced) for webhook monitoring and [MCP Custom Tools Guide](./mcp-custom-tools) for tool event integration. diff --git a/apps/platform/content/docs/meta.json b/apps/platform/content/docs/meta.json deleted file mode 100644 index 1846540d..00000000 --- a/apps/platform/content/docs/meta.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "title": "Documentation", - "pages": [ - "quickstart", - "guides", - "configuration", - "architecture", - "conventions", - "runbooks", - "api-reference" - ], - "description": "Jan Platform Documentation", - "root": true -} diff --git a/apps/platform/content/docs/quickstart.mdx b/apps/platform/content/docs/quickstart.mdx deleted file mode 100644 index 55e988f2..00000000 --- a/apps/platform/content/docs/quickstart.mdx +++ /dev/null @@ -1,351 +0,0 @@ ---- -title: "Getting Started with Jan Server" ---- - -# Getting Started with Jan Server - -Welcome! This guide will help you get Jan Server up and running in minutes. - -> **Note:** This guide covers Docker Compose setup for local development. For Kubernetes deployment (production/staging), see: -> - [Kubernetes Setup Guide](../k8s/SETUP) - Complete step-by-step Kubernetes deployment -> - [Deployment Guide](guides/deployment) - All deployment options (Kubernetes, Docker Compose, Hybrid) - -## Prerequisites - -Before you begin, ensure you have: - -- **Docker Desktop** (Windows/macOS) or **Docker Engine + Docker Compose** (Linux) -- **Make** (usually pre-installed on macOS/Linux, [install on Windows](https://gnuwin32.sourceforge.net/packages/make.htm)) -- **Git** -- At least 8GB RAM available -- For GPU inference: NVIDIA GPU with CUDA support - -Optional (for development): -- Go 1.21+ -- Go 1.23+ (for jan-cli api-test) - -## Quick Setup - -### 1. Clone the Repository - -```bash -git clone https://github.com/janhq/jan-server.git -cd jan-server -``` - -### 2. Run the Setup Wizard (Recommended) - -```bash -make quickstart -``` - -`make quickstart` launches the `jan-cli` wizard. It prompts for your LLM provider (local vLLM vs remote API), MCP search provider, and Media API preference, then writes `.env` plus `config/secrets.env`. When configuration finishes it automatically starts Docker Compose. Re-run the command anytime to update settings (answer **Y** when asked to overwrite `.env`). - -#### Wizard options at a glance -- **LLM provider**: Local vLLM (GPU, downloads models) or remote OpenAI-compatible endpoint (no GPU required). -- **MCP search**: Serper (API key), SearXNG (local, no key), or None (MCP Tools still run without search). -- **Media API**: Enable for uploads and jan_* IDs, or disable if not needed. - -#### Example configurations -- **Full local (GPU)**: Local vLLM + SearXNG + Media enabled → everything runs locally. -- **Cloud LLM + Serper**: Remote API endpoint + Serper key + Media enabled → light local footprint, best search. -- **Minimal**: Remote API endpoint + no search + Media disabled → smallest local runtime. - -### Manual configuration (if you cannot run the wizard) - -```bash -# Copy templates -cp .env.template .env -cp config/secrets.env.example config/secrets.env - -# Edit with your values -nano .env -nano config/secrets.env - -# Populate defaults and validate -make setup -``` - -`make setup` uses `jan-cli` in non-interactive mode to check dependencies, ensure directories exist, and pull base images. - -**Configuration details:** -- Canonical defaults live in `config/defaults.yaml` (generated from Go structs) -- Secrets belong in `config/secrets.env` (copied from `config/secrets.env.example`) -- Environment templates (Docker/Kubernetes) are documented in [Configuration System](configuration/) - -### 3. Start Services (skip if quickstart already did this) - -```bash -# Start full stack (CPU inference) -make up-full - -# Optional: start monitoring stack -make monitor-up -``` - -Wait for all services to start (30-60 seconds). You can monitor progress with: -```bash -make logs -``` - -### What the wizard does -1. Prompts for LLM/search/media choices. -2. Writes `.env` and `config/secrets.env`. -3. Checks Docker availability and networks. -4. Starts the Compose stack and waits for health (about 30 seconds). - -### 5. Verify Installation - -```bash -make health-check -``` - -You should see all services reporting as healthy. - -## Access Services - -Once running, you can access: - -| Service | URL | Credentials | -|---------|-----|-------------| -| **API Gateway** | http://localhost:8000 | - | -| **API Documentation** | http://localhost:8000/api/swagger/index.html | - | -| **LLM API** | http://localhost:8080 | `Authorization: Bearer ` | -| **Response API** | http://localhost:8082 | `Authorization: Bearer ` | -| **Media API** | http://localhost:8285 | `Authorization: Bearer ` | -| **MCP Tools** | http://localhost:8091 | `Authorization: Bearer ` | -| **Memory Tools** | http://localhost:8090 | `Authorization: Bearer ` | -| **Realtime API** | http://localhost:8186 | `Authorization: Bearer ` | -| **Keycloak Console** | http://localhost:8085 | admin/admin | -| **Grafana Dashboards** | http://localhost:3331 | admin/admin (after `make monitor-up`) | -| **Prometheus** | http://localhost:9090 | - (after `make monitor-up`) | -| **Jaeger Tracing** | http://localhost:16686 | - (after `make monitor-up`) | - -**Service activation**: -- vLLM starts only if you choose the local provider (GPU). Use the remote provider option for a lighter stack. -- MCP Tools and Vector Store always run; the search choice only affects which search provider is used. -- Media API is optional based on the wizard choice. - -## Your First API Call - -### 1. Get a Guest Token via Kong - -```bash -curl -X POST http://localhost:8000/llm/auth/guest-login -``` - -All traffic to `http://localhost:8000` flows through the Kong gateway, which validates Keycloak-issued JWTs or API keys (use `Authorization: Bearer ` or `X-API-Key: sk_*` headers). - -Response: -```json -{ - "access_token": "eyJhbGci...", - "refresh_token": "eyJhbGci...", - "expires_in": 300 -} -``` - -### 2. Make a Chat Completion Request - -```bash -curl -X POST http://localhost:8000/v1/chat/completions \ - -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "model": "jan-v1-4b", - "messages": [ - {"role": "user", "content": "What is the capital of France?"} - ], - "stream": false - }' -``` - -### 3. Try Streaming - -```bash -curl -X POST http://localhost:8000/v1/chat/completions \ - -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "model": "jan-v1-4b", - "messages": [ - {"role": "user", "content": "Tell me a short story"} - ], - "stream": true - }' -``` - -### 4. Use MCP Tools - -```bash -# List available tools -curl -X POST http://localhost:8000/v1/mcp \ - -H "Content-Type: application/json" \ - -d '{ - "jsonrpc": "2.0", - "id": 1, - "method": "tools/list" - }' - -# Google search -curl -X POST http://localhost:8000/v1/mcp \ - -H "Content-Type: application/json" \ - -d '{ - "jsonrpc": "2.0", - "id": 2, - "method": "tools/call", - "params": { - "name": "google_search", - "arguments": { - "q": "latest AI news" - } - } - }' -``` - -## Enable Monitoring (Optional) - -To enable full observability stack: - -```bash -make monitor-up -``` - -Access: -- **Grafana**: http://localhost:3331 (admin/admin) -- **Prometheus**: http://localhost:9090 -- **Jaeger**: http://localhost:16686 - -## Common Commands - -```bash -# View logs -make logs # All services -make logs-api # API profile (LLM, Response, Media) -make logs-mcp # MCP Tools profile - -# Check status -make health-check # Hit health endpoints -docker compose ps # Container status - -# Restart services -make restart-full # Restart everything -make restart-api # Restart API profile -make restart-llm-api # Restart only LLM API - -# Stop services -make down # Stop all containers (keeps volumes) -make down-clean # Stop containers and remove volumes -``` - -## Troubleshooting - -### Services won't start - -```bash -# Check Docker -docker --version -docker compose version - -# Check status -make health-check -docker compose ps - -# View errors -make logs - -# Full reset -make down -make down-clean -make setup -make up-full -``` - -### Port conflicts - -If you get port binding errors: - -```bash -# Check what's using ports -# Windows PowerShell: -netstat -ano | findstr "8000 8080 8085" - -# macOS/Linux: -lsof -i:8000 -lsof -i:8080 -lsof -i:8085 - -# Kill conflicting processes or change ports in .env -``` - -### vLLM GPU issues - -```bash -# Verify GPU availability -docker run --rm --gpus all nvidia/cuda:11.8.0-base-ubuntu22.04 nvidia-smi -``` - -If no GPU is detected: -- Rerun `make quickstart` and choose the remote API option (skips local vLLM) -- Or run `make up-vllm-cpu` to start the CPU-only vLLM profile when testing locally - -### Database connection errors - -```bash -# Reset database -make db-reset - -# Check database logs -docker compose logs api-db - -# Verify connection -make db-console -``` - -### API returns 401 Unauthorized - -- Check token hasn't expired (default: 5 minutes) -- Get new guest token: `curl -X POST http://localhost:8000/llm/auth/guest-login` -- Check `Authorization: Bearer ` header is set - -## Update configuration later - -```bash -make quickstart # rerun the wizard and choose Y to overwrite .env -``` - -The wizard safely reuses existing values and updates your choices without manual edits. - -## What's Next? - -Now that you have Jan Server running: - -1. **Explore the API**: - - [API Reference](api/) - - [API Examples](api/examples/) - - [Swagger UI](http://localhost:8000/api/swagger/index.html) - -2. **Learn Development**: - - [Development Guide](guides/development) - - [Development Guide - Dev-Full Mode](guides/development#dev-full-mode-hybrid-debugging) (recommended for development) - - [Testing Guide](guides/testing) - -3. **Understand Architecture**: - - [Architecture Overview](architecture/) - - [System Design](architecture/system-design) - - [Security Model](architecture/security) - -4. **Deploy to Production**: - - [Deployment Guide](guides/deployment) - - [Monitoring Guide](guides/monitoring) - -## Need Help? - -- [Full Documentation](./index) -- [Report Issues](https://github.com/janhq/jan-server/issues) -- [Discussions](https://github.com/janhq/jan-server/discussions) -- [Troubleshooting Guide](guides/troubleshooting) - ---- - -**Quick Reference**: `make help` | **All Commands**: `make help-all` diff --git a/apps/platform/content/docs/repo-naming.mdx b/apps/platform/content/docs/repo-naming.mdx deleted file mode 100644 index 26330b40..00000000 --- a/apps/platform/content/docs/repo-naming.mdx +++ /dev/null @@ -1,423 +0,0 @@ ---- -title: "Repository Naming Conventions" ---- - -# Repository Naming Conventions - -> Naming standards for the Jan monorepo structure - ---- - -## 1. Directory Naming - -### 1.1 Top-Level Directories - -Use lowercase with hyphens for multi-word names: - -``` -server/ -├── apps/ # Frontend applications -├── services/ # Backend microservices -├── packages/ # Shared libraries -├── infra/ # Infrastructure as Code -├── tools/ # Development tools -├── integrations/ # Third-party integrations -├── docs/ # Documentation -└── tests/ # Test suites -``` - -**Rules:** -- Use descriptive, plural nouns where appropriate (`apps`, `services`, `packages`) -- Single word preferred (`tools`, `docs`) -- Hyphenate multi-word directories (`infra-scripts` if needed) -- No underscores in directory names - -### 1.2 Application Directories - -Frontend apps in `apps/` use kebab-case: - -``` -apps/ -├── web/ # Main web application -├── admin/ # Admin dashboard -├── platform/ # Platform interface -├── mobile/ # Mobile app (future) -└── chrome-extension/ # Browser extension (example) -``` - -**Rules:** -- Lowercase only -- Hyphenate multi-word apps (`chrome-extension`, `user-portal`) -- Descriptive and concise -- Avoid abbreviations unless widely known - -### 1.3 Service Directories - -Backend services in `services/` use kebab-case with suffix pattern: - -``` -services/ -├── llm-api/ # LLM API service -├── mcp-tools/ # MCP tools service -├── media-api/ # Media API service -├── memory-tools/ # Memory tools service -├── realtime-api/ # Realtime API service -└── response-api/ # Response API service -``` - -**Rules:** -- Lowercase kebab-case -- End with `-api` or `-tools` or `-service` for clarity -- Match Docker image naming -- Consistent across documentation - -### 1.4 Package Directories - -Shared packages in `packages/` use kebab-case with prefix pattern: - -``` -packages/ -├── shared-ui/ # Shared React components -├── shared-types/ # TypeScript type definitions -├── shared-utils/ # Common utilities -├── shared-config/ # Shared configuration -└── go-common/ # Shared Go packages -``` - -**Rules:** -- Prefix with `shared-` for cross-app packages -- Language-specific packages use language name (`go-common`, `py-utils`) -- Descriptive suffix (`-ui`, `-types`, `-utils`) - ---- - -## 2. File Naming - -### 2.1 Configuration Files - -``` -.env # Root environment file -.env.example # Example environment template -.gitignore # Git ignore rules -docker-compose.yml # Main compose file -pnpm-workspace.yaml # pnpm workspace config -turbo.json # Turborepo config (if used) -``` - -**Rules:** -- Lowercase -- Use standard config file names -- Hyphenate multi-word configs (`docker-compose.yml`) - -### 2.2 Documentation Files - -``` -docs/ -├── README.md # Documentation index -├── quickstart.md # Getting started guide -├── repo-naming.md # This document -└── api/ - ├── README.md - └── llm-api.md -``` - -**Rules:** -- Lowercase kebab-case with `.md` extension -- `README.md` (uppercase) for index files -- Descriptive names (`authentication.md`, `deployment-guide.md`) -- Match directory structure where relevant - -### 2.3 Source Code Files - -**TypeScript/JavaScript:** -- Components: `PascalCase.tsx` (e.g., `UserProfile.tsx`) -- Utilities: `camelCase.ts` (e.g., `formatDate.ts`) -- Hooks: `camelCase.ts` with `use` prefix (e.g., `useAuth.ts`) -- Types: `PascalCase.types.ts` (e.g., `User.types.ts`) - -**Go:** -- Files: `snake_case.go` (e.g., `cmd_setup.go`, `utils.go`) -- Packages: lowercase single word (e.g., `config`, `telemetry`) - ---- - -## 3. Docker & Infrastructure - -### 3.1 Docker Compose Files - -``` -infra/docker/ -├── infrastructure.yml # Core infrastructure -├── services-api.yml # API services -├── services-mcp.yml # MCP services -├── services-memory.yml # Memory services -├── services-realtime.yml # Realtime services -└── observability.yml # Monitoring stack -``` - -**Pattern**: `{category}.yml` or `services-{type}.yml` - -**Rules:** -- Lowercase kebab-case -- Group by function/category -- Prefix with `services-` for service definitions -- Single word for infrastructure types - -### 3.2 Docker Images - -**Format**: `registry.domain.com/server/{service}:{tag}` - -**Examples:** -- `registry.menlo.ai/server/llm-api:dev-abc123` -- `registry.menlo.ai/server/mcp-tools:prod-v1.2.3` - -**Rules:** -- Use kebab-case for service names -- Tag format: `{env}-{identifier}` (e.g., `dev-sha`, `prod-v1.0.0`) - -### 3.3 Kubernetes Resources - -``` -infra/k8s/ -├── namespace.yaml -├── llm-api-deployment.yaml -├── mcp-tools-service.yaml -└── ingress.yaml -``` - -**Pattern**: `{resource-name}-{type}.yaml` or `{type}.yaml` - -**Rules:** -- Lowercase kebab-case -- Suffix with resource type (`-deployment`, `-service`, `-configmap`) -- Standalone types use singular (`namespace.yaml`, `ingress.yaml`) - ---- - -## 4. Git Branch Naming - -### 4.1 Branch Types - -**Format**: `{type}/{scope}-{description}` or `{type}/{description}` (for cross-cutting changes) - -**Types:** -- `feat/` - New features -- `fix/` - Bug fixes -- `refactor/` - Code refactoring -- `docs/` - Documentation updates -- `chore/` - Maintenance tasks -- `test/` - Test additions/fixes - -**Scopes** (optional but recommended for monorepo): -- `web` - Web app changes -- `admin` - Admin app changes -- `platform` - Platform app changes -- `services` - Backend services (general) -- `llm-api`, `mcp-tools`, etc. - Specific service -- `shared` - Shared packages -- `infra` - Infrastructure changes - -**Examples:** - -*With scope (recommended for monorepo):* -- `feat/web-user-authentication` - New auth feature in web app -- `fix/llm-api-memory-leak` - Bug fix in LLM API service -- `feat/admin-dashboard-redesign` - Admin app feature -- `refactor/shared-refactor-types` - Shared package refactoring -- `chore/infra-update-k8s-configs` - Infrastructure update - -*Without scope (for cross-cutting changes):* -- `docs/update-api-guide` - Documentation affecting multiple services -- `chore/upgrade-dependencies` - Dependency updates across monorepo -- `refactor/monorepo-structure` - Structural changes - -### 4.2 Environment Branches - -- `main` - Main development branch (triggers dev deployments) -- `dev-test` - Testing branch (triggers dev deployments) -- `release` - Production branch (triggers prod deployments) - ---- - -## 5. Package Naming - -### 5.1 NPM Packages - -**Format**: `@jan/{package-name}` - -**Examples:** -- `@jan/shared-ui` -- `@jan/shared-types` -- `@jan/shared-utils` - -### 5.2 Docker Images - -**Format**: `server/{service-name}:{tag}` - -**Examples:** -- `server/llm-api:latest` -- `server/mcp-tools:dev-abc123` - -### 5.3 Go Modules - -**Format**: `github.com/janhq/server/{path}` - -**Examples:** -- `github.com/janhq/server/tools/jan-cli` -- `github.com/janhq/server/pkg/config` - ---- - -## 6. GitHub Actions Workflows - -### 6.1 Workflow Structure - -All workflows live in `.github/workflows/` with the following organization: - -``` -.github/workflows/ -├── ci-backend-dev.yml # Backend services (dev/main branches) -├── ci-backend-prod.yml # Backend services (release branch) -├── ci-app-web-dev.yml # Web app dev/main -├── ci-app-web-prod.yml # Web app release -├── ci-app-admin-dev.yml # Admin app dev/main -├── ci-app-admin-prod.yml # Admin app release -├── ci-app-platform-dev.yml # Platform app dev/main -├── ci-app-platform-prod.yml # Platform app release -├── ci-packages.yml # Shared packages validation -├── config-drift.yml # Config validation -└── _reusable-docker.yml # Reusable Docker build template -``` - -### 6.2 Naming Pattern - -**Format**: `ci-{component}-{environment}.yml` - -**Components:** -- `backend` - Backend microservices -- `app-{name}` - Frontend applications (web, admin, platform) -- `packages` - Shared packages/libraries -- Component-specific names (e.g., `config-drift`) - -**Environments:** -- `dev` - Development/staging (triggers on `main`, `dev-test` branches) -- `prod` - Production (triggers on `release` branch) -- No suffix - Environment-agnostic validation workflows - -**Special Prefixes:** -- `_` (underscore) - Reusable workflow templates (e.g., `_reusable-docker.yml`) - -### 6.3 Examples - -#### ✅ Correct: -- `ci-backend-dev.yml` - Backend services for dev environment -- `ci-app-web-prod.yml` - Web app production deployment -- `ci-app-checkout-dev.yml` - New checkout app dev workflow -- `ci-packages.yml` - Shared packages validation -- `_reusable-node-build.yml` - Reusable Node.js build template - -#### ❌ Incorrect: -- `dev.yml` - Too generic, unclear what it builds -- `web-app-ci.yml` - Wrong order, should be `ci-app-web-*.yml` -- `ci-web.yml` - Missing `app-` prefix for frontend apps -- `backend.yml` - Missing `ci-` prefix and environment suffix -- `reusable-docker.yml` - Missing underscore prefix - -### 6.4 Adding New App Workflows - -When adding a new frontend app (e.g., `mobile`), create: - -``` -.github/workflows/ -├── ci-app-mobile-dev.yml # Mobile app dev/main -└── ci-app-mobile-prod.yml # Mobile app release -``` - -**Template for dev workflow:** -```yaml -name: CI - Mobile App (Dev) - -on: - push: - branches: [main, dev-test] - paths: - - 'apps/mobile/**' - - 'packages/shared-ui/**' - - 'packages/shared-types/**' - - 'packages/shared-utils/**' - pull_request: - paths: - - 'apps/mobile/**' - -jobs: - build-and-test: - # ... implementation -``` - ---- - -## 7. Quick Reference - -### Adding a New Frontend App - -1. **Directory**: `apps/new-app/` -2. **Workflows**: - - `ci-app-new-app-dev.yml` - - `ci-app-new-app-prod.yml` -3. **Package**: `@jan/new-app` -4. **Branch**: `feat/add-new-app` - -### Adding a New Backend Service - -1. **Directory**: `services/new-service/` -2. **Workflow**: Include in `ci-backend-dev.yml` and `ci-backend-prod.yml` -3. **Docker**: `server/new-service:tag` -4. **Compose**: Add to `infra/docker/services-api.yml` - -### Adding a New Shared Package - -1. **Directory**: `packages/shared-new/` -2. **Workflow**: Include in `ci-packages.yml` -3. **NPM**: `@jan/shared-new` -4. **Workspace**: Auto-detected by `pnpm-workspace.yaml` - ---- - -## 8. Validation Checklist - -Before committing new files/directories, verify: - -- [ ] Names follow kebab-case (lowercase with hyphens) -- [ ] Workflow files use `ci-{component}-{env}.yml` pattern -- [ ] Reusable workflows prefixed with underscore -- [ ] Directories use plural nouns where appropriate -- [ ] No spaces or special characters (except `-` and `_`) -- [ ] Consistent with existing naming patterns -- [ ] Git branch follows `{type}/{description}` format -- [ ] Package names use organization scope (`@jan/`) - ---- - -## 9. Migration Guide - -When migrating an existing app into the monorepo: - -1. **Rename directory** to match `apps/` or `services/` convention -2. **Create workflows** following `ci-app-{name}-{env}.yml` pattern -3. **Update package.json** name to `@jan/{name}` if applicable -4. **Add to workspace** in `pnpm-workspace.yaml` (auto if in `apps/`) -5. **Update imports** to use monorepo package references -6. **Document** in `docs/` with matching kebab-case filename - ---- - -## References - -- [Monorepo Best Practices](https://monorepo.tools/) -- [Semantic Versioning](https://semver.org/) -- [Conventional Commits](https://www.conventionalcommits.org/) -- [GitHub Actions Naming Best Practices](https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions) - ---- - -**Questions?** Ask in #engineering or open an issue with the `documentation` label. diff --git a/apps/platform/content/docs/roadmap.mdx b/apps/platform/content/docs/roadmap.mdx deleted file mode 100644 index 374b5564..00000000 --- a/apps/platform/content/docs/roadmap.mdx +++ /dev/null @@ -1,503 +0,0 @@ ---- -title: "Jan Server Roadmap" ---- - -# Jan Server Roadmap - -> **TL;DR:** Building an AI-powered todo/notes app with autonomous agents that can research, plan, write, and collaborate. Open source core + commercial Enterprise Edition. Powered by local Jan models (jan-v2, jan-v3). - ->Self-hosted agentic AI platform powered by local JAN models - - ---- - -## Vision - -**Jan Server** = Production-ready agentic AI platform powered by local Jan models (jan-v2, jan-v3) for building sophisticated workflows where autonomous agents plan, reflect, collaborate, and execute complex tasks. The leading local-first SaaS platform where agents continuously improve through intelligent orchestration of pluggable microservices and optimized Jan models. - -**Hero Product:** AI-powered todo & notes app where agents autonomously work on your tasks using local Jan models for privacy, speed, and cost efficiency. - -**Model Strategy:** Jan models first - optimized for agentic workflows with fine-tuning for planning and reflection. Remote providers as optional fallback. - -**Self-hosted AI** -We are building **local-first AI infrastructure**: - -- Users should be able to run useful AI workloads on low-spec machines. -- Teams and companies should be able to run scalable, multi-user AI backends on their own servers. -- The **same OpenAI-compatible API** should work in both cases (and in tests against cloud backends). - -Cloud is supported, but the **future is local AI setup**: -- Local inference is the default. -- Remote endpoints are helpers for testing/benchmarking/overflow — not a requirement. ---- - -## Architecture - -**Open Source Core** (Apache 2.0): -- Agent orchestration, planning, memory, reflection -- Tool ecosystem (web search, content generation, integrations) - -**Enterprise Edition** (`/ee`, Commercial): -- Multi-tenancy, usage tracking, billing -- Team collaboration, SSO, RBAC -- Enterprise features (on-premise, custom SLAs) - -**Local inference is mandatory** -- Every valid Jan Server deployment must have **at least one local model runtime** configured. -- The system should **start and remain usable offline** as long as local models are available. -- Remote endpoints (OpenAI, Together, company cloud Jan, etc.) are optional extras. ---- - -### 2. Two local setups: Lite vs Heavy (both with local inference) - -### A. Lightweight local setup (low hardware, but still offline-capable) - -Target: laptops, NUCs, old desktops, maybe 8–16 GB VRAM, - -**Characteristics:** - -* One **small, quantized model** (e.g. 7B/8B, Q4/Q5) running locally. -* CPU or tiny GPU/NPU backend (lite vllm via Jan, etc.). -* Minimal services: - - * `llm-api` with a **“lite” runtime** (vllm style) - * `response-api` (optional, depending how integrated you want tools/agents) - * Local storage (SQLite or embedded DB) -* Skips: - - * Kong / Keycloak / full auth stack - * Heavy observability - - -### B. Heavy local setup (company / homelab server) - -Target: multi-user, multi-model, GPUs, bigger RAM. Still **no requirement** for external APIs. - -**Characteristics:** - -* vLLM or similar GPU runtime(s) -* Multiple local models: - - * Small fast ones - * Big accurate ones -* Full stack: - - * Kong gateway - * Keycloak / SSO - * `llm-api`, `response-api`, `media-api`, `mcp-tools` - * Monitoring (Prometheus / Grafana / Jaeger) ---- -## Roadmap (2026 Focus) - -### Phase 1: Foundation (Completed - Q4 2025) - -**Status:** ✅ Complete - -#### Microservices Architecture -- ✅ LLM API Service (chat completions, conversations, projects) -- ✅ Media API Service (content ingestion, deduplication, jan_* IDs) -- ✅ Response API Service (multi-step orchestration) -- ✅ MCP Tools Service (Model Context Protocol integration) -- ✅ Kong Gateway (centralized routing and auth) -- ✅ Keycloak (OIDC authentication) - -#### Infrastructure -- ✅ Docker Compose with profiles -- ✅ Kubernetes/Helm charts -- ✅ PostgreSQL 18 database -- ✅ S3-compatible object storage (encrypted at rest) -- ✅ OpenTelemetry observability stack - -#### Security Foundation -- ✅ Keycloak OIDC authentication -- ✅ JWT-based authorization at gateway -- ✅ API key authentication - -#### Developer Experience -- ✅ 100+ Makefile commands -- ✅ Comprehensive testing suite (6 test collections) -- ✅ Service template system -- ✅ Documentation (20+ pages) - -#### Tool Integration -- ✅ Google Search (Serper API) -- ✅ Web scraping (Serper API) -- ✅ Code execution (SandboxFusion) -- ✅ Vector store integration - -#### Model Support -- ✅ vLLM inference engine -- ✅ Jan-v2 model integration (local, initiat for agentic workflows) - ---- - -### Phase 2: Agentic Core + Tools (Q1 2026) - -**Status:** In Progress - -**Hero Workflow:** "Plan Product Launch" - Agent decomposes task → researches → creates timeline → drafts announcement → reviews quality - -**Core Services:** -- **Agent Orchestration** - Lifecycle, state, events (Kafka) -- **Planning Service** - Task decomposition, dependency graphs, execution -- **Reflection Service** - Self-critique, quality scoring, iterative refinement -- **Memory Service** - Short-term (Redis), long-term (vector DB), episodic memory - -**Model Optimization:** -- Jan-v2 fine-tuning for planning and reflection tasks -- Model selection per agent type (jan-v2 | jan-v3, for orchestration, for reasoning) -- Local-first inference with remote fallback -- Model caching and optimization for agent workflows - -**Tool Ecosystem (12-15 tools):** -1. **Web Research** - Serper, SearXNG, scraping, article extraction -2. **Content Tools** - Markdown, grammar check, citations, readability -3. **Integrations** - Calendar, email, Slack, GitHub -4. **Computer Use** - MCP Computer Use extension for desktop automation ( experimentals) -5. **Browser Automation** - MCP Browser extension for web interactions -6. **Knowledge Base** - Personal knowledge base system for user document management - -**Security & Privacy:** -- Client-side encryption for sensitive data (tasks, notes, memory) -- Encrypted storage with tenant-owned keys (BYOK - Bring Your Own Key) -- Data isolation per user/workspace -- No training on user data policy (contractual guarantee) -- Audit logging for all agent actions - -**Todo App Features:** -- Task/note creation, agent assignment, progress tracking -- Task decomposition, content extraction, note linking -- 12-15 tools agents can use, tool inspector UI -- End-to-end encryption for task data - -**Exit Criteria:** Hero workflow 85% success, memory persists, 5+ step workflows, 90% of research tasks use tools, encryption enabled - ---- - -### Phase 2.5: Early SaaS + Security (Q1 - Q2 2026) **[EE Module]** - -**Status:** Planned - -**Goal:** Enable early paid users with basic commercial features and enterprise security - -**Features** (`/ee`): -- Multi-tenancy: org/workspace isolation, basic RBAC -- Usage tracking: API calls, tokens, agent tasks, storage -- Billing: Stripe integration, pricing models: TBD - -**Security & Compliance:** -- OAuth 2.0 integrations (Google, Microsoft, GitHub) -- SSO with SAML 2.0 support -- Encrypted storage with AES-256 encryption -- Tenant-owned encryption keys (BYOK) -- Data residency options (US, EU, APAC) -- SOC 2 Type I audit initiated -- Privacy policy: No training on customer data (contractual) -- Connector data isolation (Slack, GitHub, Calendar data never used for training) - -**Exit Criteria:** usage tracking accurate, zero billing disputes, SOC 2 Type I audit in progress - ---- - -### Phase 3: SaaS Platform + Compliance (Q2-Q3 2026) **[EE Module]** - -**Status:** Planned - -**Pricing:** -TBD - -**Features:** -- Unified SaaS Service (tenant + billing + usage + cost mgmt) -- Real-time collaboration, knowledge base -- Workflow automation (recurring, triggered, templates) -- Integration marketplace (Google, Slack, GitHub, Notion) - -**Enterprise Security & Compliance:** -- **SOC 2 Type II certification** (audit complete) -- OAuth 2.0 + SAML 2.0 for all major providers -- Client-side encryption with tenant-managed keys -- Hardware Security Module (HSM) support -- Data isolation: Connector data never used for model training -- GDPR compliance with data deletion APIs -- HIPAA compliance option (for healthcare use cases) -- Audit logs with tamper-proof storage -- IP whitelisting and VPC peering -- Custom data retention policies -- Annual penetration testing reports - -**Data Privacy Guarantees:** -- User data encrypted at rest (AES-256) and in transit (TLS 1.3) -- Zero-knowledge architecture option (tenant owns encryption keys) -- Explicit opt-in for any data analytics -- No training on customer data (legally binding contract) -- Connector data (Slack, GitHub, Calendar, etc.) remains isolated -- Data residency compliance (US, EU, UK, APAC regions) -- Right to be forgotten (complete data deletion within 30 days) - ---- -## Use Cases - -### 1. Plan Product Launch -User: "Plan Q2 product launch" -- Agent breaks down: research → analysis → timeline → budget → messaging -- Research agent gathers competitor info -- Planning agent creates detailed schedule -- Writing agent drafts announcement -- Output: Structured notes with research, timeline, draft content - -### 2. Meeting Notes → Action Items -User: Pastes meeting transcript -- Agent extracts decisions, action items, deadlines -- Auto-creates linked todos with tags -- Sets reminders based on urgency - -### 3. Research & Write Blog Post -User: "Write blog post: The Future of Agentic AI" -- Research agent finds papers, articles, trends -- Content agent creates outline -- Writing agent drafts 1,500 words with citations -- Reflection agent fact-checks and reviews -- Output: Publication-ready blog post - -### 4. Organize Chaotic Notes -User: Has 50+ unorganized notes -- Agent analyzes themes and relationships -- Creates taxonomy and tags -- Suggests merges/splits -- Builds knowledge graph -- Generates summary - -### 5. Investor Pitch Prep -User: "Prepare Series A pitch deck" -- Agent breaks down: market size, traction, team, financials -- Data agent pulls metrics from integrated tools -- Research agent finds market data -- Writing agent drafts narrative -- Output: Deck outline with supporting data - ---- - -## Key Differentiators - -**vs. LangGraph/AutoGen/CrewAI:** -- Local-first with Jan models (jan-v2/jan-v3 optimized for agentic workflows) -- Production-first (multi-tenancy, billing, SLA built-in) -- Pluggable architecture (swap models/tools without code changes) -- Native observability (distributed tracing for every agent step) -- Marketplace ecosystem (community agents/tools ready to deploy) -- Enterprise-ready (SSO, RBAC, compliance from day one) -- Security-first (client-side encryption, tenant-owned keys, zero-knowledge option) -- Privacy-guaranteed (no training on customer data, connector data isolation) -- Compliance-certified (SOC 2 Type II, GDPR, optional HIPAA) - ---- - -## Agentic Patterns - -1. **Reflection** – Agents review and improve their own outputs -2. **Planning** – Break complex tasks into executable steps -3. **Tool Use** – Intelligent tool discovery and execution -4. **Memory** – Context persists across interactions -5. **User Modeling / Profiling** – Agents learn user preferences, behavior, and persona over time to personalize interactions - ---- - -## Tech Stack - -**Services:** Go, Gin, zerolog -**Gateway:** Kong 3.5 -**Auth:** Keycloak (OIDC, OAuth 2.0, SAML 2.0) -**Database:** PostgreSQL 18 -**Memory:** Redis + Qdrant (vector store) -**Inference:** Jan-v2 (local, primary) | Jan-v3 (upcoming) | vLLM engine | Remote providers (fallback) -**Models:** Jan-v2 for reasoning/orchestration/planning, Jan-v3 for advanced reasoning -**Observability:** OpenTelemetry, Prometheus, Grafana, Jaeger -**Events:** Kafka -**Security:** AES-256 encryption, TLS 1.3, HashiCorp Vault (key management) -**Compliance:** Audit logging, tamper-proof storage, data residency controls -**Deployment:** Docker Compose, Kubernetes/Helm - ---- - -## Repository Structure - -``` -jan-server/ -├── services/ # Open Source (Apache 2.0) -│ ├── llm-api/ -│ ├── response-api/ -│ ├── media-api/ -│ ├── mcp-tools/ -│ ├── planning-service/ # Phase 2 -│ ├── reflection-service/ # Phase 2 -│ └── memory-tools/ # Phase 2 -├── ee/ # Enterprise Edition (Commercial) -│ └── services/ -│ ├── tenant-service/ # Multi-tenancy -│ ├── usage-service/ # Usage tracking -│ ├── billing-service/ # Billing & payments -│ └── saas-service/ # Unified SaaS platform -├── pkg/ # Shared packages -├── k8s/ # Kubernetes/Helm charts -└── docs/ # Documentation -``` - ---- - -## Status Summary - -| Phase | Status | Timeline | Key Deliverable | -|-------|--------|----------|----------------| -| Phase 1: Foundation | Complete | Q4 2025 | Microservices + infra + basic security | -| Phase 2: Agentic Core + Tools | In Progress | Q1 2026 | Planning + Memory + Reflection + 12-15 tools + encryption | -| Phase 2.5: Early SaaS + Security | Planned | Q2 2026 | Multi-tenancy + Billing + OAuth + BYOK + SOC 2 Type I | -| Phase 3: SaaS Platform + Compliance | Planned | Q3-Q4 2026 | Team tier + workflows + marketplace + SOC 2 Type II | - ---- - - -## Service Architecture Flow - -### Current Architecture (Phase 1) - -``` -┌─────────────────────────────────────────────────────┐ -│ Client / SDK │ -└────────────────────┬────────────────────────────────┘ - │ - ▼ - ┌───────────────────────┐ - │ Kong Gateway (8000) │ - └───────┬───────────────┘ - │ - ┌───────────┼───────────┬───────────┐ - │ │ │ │ - ▼ ▼ ▼ ▼ -┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ -│ LLM API │ │Response │ │ Media │ │ MCP │ -│ (8080) │ │ (8082) │ │ (8285) │ │ (8091) │ -└────┬────┘ └────┬────┘ └────┬────┘ └────┬────┘ - │ │ │ │ - └───────────┴───────────┴───────────┘ - │ - ┌───────────┴───────────┬───────────────┐ - │ │ │ - ▼ ▼ ▼ -┌──────────┐ ┌──────────┐ ┌──────────┐ -│PostgreSQL│ │ S3/ │ │ vLLM │ -│ (DB) │ │ Storage │ │ (8101) │ -└──────────┘ └──────────┘ └──────────┘ -``` - -### Target Architecture - -``` -┌─────────────────────────────────────────────────────┐ -│ Client / SDK / Workflow Engine │ -└────────────────────┬────────────────────────────────┘ - │ - ▼ - ┌───────────────────────┐ - │ Kong Gateway (8000) │ - │ + Auth + Rate Limit │ - └───────┬───────────────┘ - │ - ┌───────────┼─────────────────────────────────────┐ - │ Agent Orchestration Layer │ - │ ┌─────────────────────────────────────┐ │ - │ │ Agent Coordinator (8095) │ │ - │ │ - Team Formation │ │ - │ │ - Task Delegation │ │ - │ │ - Communication Bus │ │ - │ └────────┬────────────────────────────┘ │ - │ │ │ - │ ┌────────┴────────┬─────────────┬──────────┐ │ - │ │ │ │ │ │ - │ ▼ ▼ ▼ ▼ │ - │ Research Code Agent Data Agent Creative│ - │ Agent Agent│ - └─────────────────────────────────────────────────┘ - │ - ┌───────────────┼────────────────────────────┐ - │ Core Services Layer │ - │ │ - │ ┌────────┐ ┌────────┐ ┌────────┐ │ - │ │Planning│ │Reflect.│ │ Memory │ │ - │ │ (8092) │ │ (8093) │ │ (8094) │ │ - │ └───┬────┘ └───┬────┘ └───┬────┘ │ - │ │ │ │ │ - │ ┌───┴───────────┴───────────┴───┐ │ - │ │ │ │ - │ │ Existing Services Layer │ │ - │ │ ┌──────┐ ┌──────┐ ┌──────┐ │ │ - │ │ │ LLM │ │Media │ │ MCP │ │ │ - │ │ │(8080)│ │(8285)│ │(8091)│ │ │ - │ │ └──────┘ └──────┘ └──────┘ │ │ - │ └────────────────────────────────┘ │ - └─────────────────────────────────────────────┘ - │ - ┌───────────────┼────────────────────────────┐ - │ SaaS Layer [EE: /ee] (Optional) │ - │ Enabled via: EE_ENABLED=true │ - │ │ - │ ┌────────┐ ┌────────┐ ┌────────┐ │ - │ │ Tenant │ │ Usage │ │Billing │ │ - │ │ Mgmt │ │ Track │ │Payment │ │ - │ │ (8100) │ │ (8101) │ │ (8102) │ │ - │ └───┬────┘ └───┬────┘ └───┬────┘ │ - │ │ │ │ │ - │ ┌───┴───────────┴───────────┴────┐ │ - │ │ Security │ Cost Mgmt │ Admin │ │ - │ │ (8103) │ (8104) │ (8108) │ │ - │ └────────────────────────────────┘ │ - │ │ - │ License: Commercial (source-available) │ - └─────────────────────────────────────────────┘ - │ - ┌───────────────┼────────────────────────────┐ - │ Tool & Intelligence Layer │ - │ │ - │ ┌────────┐ ┌────────┐ ┌──────────┐ │ - │ │ Tool │ │Meta- │ │Knowledge │ │ - │ │Registry│ │Learning│ │ Graph │ │ - │ │ (8096) │ │ (8105) │ │ (8099) │ │ - │ └───┬────┘ └───┬────┘ └────┬─────┘ │ - │ │ │ │ │ - │ ┌───┴───────────┴────────────┴─────┐ │ - │ │ Marketplace (8106) │ Analytics │ │ - │ │ Agents·Tools·Flows│ (8107) │ │ - │ └────────────────────────────────────┘ │ - │ ┌────────────────────────────────────┐ │ - │ │ External Tools Ecosystem │ │ - │ │ • Search • Code Exec • DB │ │ - │ │ • APIs • Files • Comm │ │ - │ └────────────────────────────────────┘ │ - └─────────────────────────────────────────────┘ - │ - ┌───────────────┴────────────────────────────┐ - │ Data & Infrastructure Layer │ - │ │ - │ ┌──────────┐ ┌──────────┐ ┌─────────┐ │ - │ │PostgreSQL│ │ Redis │ │ Vector │ │ - │ │ (Multi- │ │ (Memory) │ │ Store │ │ - │ │ Tenant) │ │ │ │ (Qdrant)│ │ - │ └──────────┘ └──────────┘ └─────────┘ │ - │ │ - │ ┌──────────┐ ┌──────────┐ ┌─────────┐ │ - │ │ S3 │ │ vLLM │ │ Kafka │ │ - │ │ Storage │ │ (8101) │ │ (Events)│ │ - │ └──────────┘ └──────────┘ └─────────┘ │ - │ │ - │ ┌──────────┐ ┌──────────┐ │ - │ │ Stripe │ │ PayPal │ (Payments) │ - │ └──────────┘ └──────────┘ │ - └─────────────────────────────────────────────┘ - │ - ┌───────────────┴────────────────────────────┐ - │ Observability Layer │ - │ │ - │ Prometheus • Grafana • Jaeger • OTel │ - │ Logs • Traces • Metrics • Alerts │ - │ Cost Tracking • Usage Analytics │ - └─────────────────────────────────────────────┘ -``` diff --git a/apps/platform/content/docs/runbooks/index.mdx b/apps/platform/content/docs/runbooks/index.mdx deleted file mode 100644 index 7f5d4a1d..00000000 --- a/apps/platform/content/docs/runbooks/index.mdx +++ /dev/null @@ -1,20 +0,0 @@ ---- -title: "Runbooks" ---- - -# Runbooks - -On-call playbooks for operating Jan Server. Use these when responding to incidents or performing operational tasks that must be repeatable and low-risk. - -## Available runbooks -- [Monitoring](./monitoring) - Health checks, dashboards, alerts, tracing, and log capture steps. - -## When to use guides vs runbooks -- **Runbooks**: Pager/on-call situations, incident response, repetitive operational procedures. -- **Guides**: Learning, setup, and exploratory workflows (see `../guides/`). - -## Contributing -- Keep steps concise and command-first. -- Include prerequisites (access, permissions, tools). -- Add rollback/verification steps and expected outcomes. -- Link back to the relevant guide for deeper context. diff --git a/apps/platform/content/docs/runbooks/meta.json b/apps/platform/content/docs/runbooks/meta.json deleted file mode 100644 index bce9138c..00000000 --- a/apps/platform/content/docs/runbooks/meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "title": "Runbooks", - "pages": ["index", "monitoring"] -} diff --git a/apps/platform/content/docs/runbooks/monitoring.mdx b/apps/platform/content/docs/runbooks/monitoring.mdx deleted file mode 100644 index 9c820704..00000000 --- a/apps/platform/content/docs/runbooks/monitoring.mdx +++ /dev/null @@ -1,406 +0,0 @@ ---- -title: "Jan Server Monitoring Runbook" ---- - -# Jan Server Monitoring Runbook - -## Quick Reference - -| Alert | Severity | MTTR Target | On-Call Action | -|-------|----------|-------------|----------------| -| HighLLMLatency | Warning | 15min | [§1](#1-high-llm-latency) | -| QueueBacklog | Critical | 5min | [§2](#2-queue-backlog) | -| CollectorDown | Critical | 2min | [§3](#3-collector-outage) | -| StorageFailure | Critical | 10min | [§4](#4-media-api-storage-failure) | -| TraceExportFailure | Warning | 30min | [§5](#5-trace-export-failure) | -| ClassifierErrors | Warning | 20min | [§6](#6-conversation-classifier-errors) | - ---- - -## 1. High LLM Latency - -**Alert:** `HighLLMLatency` -**Triggered when:** P95 LLM API latency >2s for 5min -**Impact:** Degraded user experience, potential timeouts, increased abandonment rate - -### Investigation Steps - -1. **Check LLM Provider Dashboard** - ```bash - # Open Grafana - open https://grafana/d/llm-overview - ``` - - Review latency by model (GPT-4 vs GPT-3.5) - - Check error rates per provider - -2. **Verify Upstream Provider Status** - - OpenAI: https://status.openai.com - - Anthropic: https://status.anthropic.com - - Azure: https://status.azure.com - -3. **Check Recent Deployments** - ```bash - git log --since="1 hour ago" --oneline - kubectl rollout history deployment/llm-api - ``` - -4. **Inspect Token Queue Depth** - ```bash - curl localhost:8080/metrics | grep queue_depth - ``` - -5. **Review Jaeger Traces** - - Find slow traces: `http://jaeger:16686/search?service=llm-api&minDuration=2s` - - Look for database queries, external API calls taking >1s - -### Remediation - -**If Provider Issue:** -```bash -# Enable fallback provider -jan-cli config set llm.fallback_enabled=true -jan-cli config set llm.fallback_provider=anthropic -``` - -**If Jan Server Issue:** -```bash -# Scale replicas -kubectl scale deployment/llm-api --replicas=5 - -# If memory exhaustion -kubectl top pod -l app=llm-api -kubectl set resources deployment/llm-api --limits=memory=2Gi -``` - -**If Database Bottleneck:** -```sql --- Check connection pool -psql jan_server -c "SELECT COUNT(*), state FROM pg_stat_activity GROUP BY state;" - --- Check slow queries -psql jan_server -c "SELECT query, mean_exec_time FROM pg_stat_statements ORDER BY mean_exec_time DESC LIMIT 10;" -``` - -### Escalation - -- **After 30min:** Page SRE team lead via PagerDuty -- **After 1h:** Engage vendor support (OpenAI/Anthropic) -- **If P0:** Notify customer success team for user communication - ---- - -## 2. Queue Backlog - -**Alert:** `ResponseAPIQueueBacklog` -**Triggered when:** Response API queue depth >100 for 10min -**Impact:** Processing delays, webhook failures, incomplete conversations - -### Root Causes - -- Background worker pool exhausted -- Template API latency spike -- Media API unavailable -- Database connection pool exhausted - -### Investigation - -```bash -# Check worker status -curl http://response-api:8081/metrics | grep workers_active -curl http://response-api:8081/metrics | grep workers_idle - -# View queue contents -psql jan_server -c "SELECT COUNT(*), status, error_message FROM background_jobs GROUP BY status, error_message ORDER BY COUNT(*) DESC;" - -# Check dependent services -make health-check - -# View recent job failures -psql jan_server -c "SELECT id, status, error_message, created_at FROM background_jobs WHERE status='failed' ORDER BY created_at DESC LIMIT 20;" -``` - -### Remediation - -1. **Increase Worker Pool** - ```bash - kubectl set env deployment/response-api WORKER_POOL_SIZE=20 - kubectl rollout status deployment/response-api - ``` - -2. **Purge Old Jobs** - ```bash - jan-cli jobs purge --older-than=1h --status=failed - jan-cli jobs retry --status=failed --max-retries=3 - ``` - -3. **Restart Service (Last Resort)** - ```bash - kubectl rollout restart deployment/response-api - kubectl rollout status deployment/response-api - ``` - ---- - -## 3. Collector Outage - -**Alert:** `OTELCollectorDown` -**Triggered when:** Collector unreachable for 2min -**Impact:** Loss of observability (no new traces/metrics), blind operations - -### Symptoms - -- Grafana dashboards flatline -- Jaeger UI shows no recent traces -- Services log OTLP export errors - -### Investigation - -```bash -# Check collector health -curl http://otel-collector:13133/ - -# View collector logs -kubectl logs -l app=otel-collector --tail=100 - -# Check resource usage -kubectl top pod -l app=otel-collector - -# Verify connectivity from services -kubectl run -it --rm debug --image=curlimages/curl --restart=Never \ - -- curl -v http://otel-collector:4318/v1/traces -``` - -### Remediation - -1. **Restart Collector** - ```bash - kubectl rollout restart deployment/otel-collector - kubectl rollout status deployment/otel-collector - ``` - -2. **If Resource Exhaustion** - ```bash - # Increase memory - kubectl set resources deployment/otel-collector --limits=memory=1Gi - - # Check Jaeger backend - curl http://jaeger-query:16686/api/services - ``` - -3. **If Configuration Error** - ```bash - # Validate config - kubectl get configmap otel-collector-config -o yaml | yq '.data' - - # Revert to last known good config - kubectl rollout undo deployment/otel-collector - ``` - -### Fallback Mode - -Services continue operating without telemetry until collector is restored. No user impact. - ---- - -## 4. Media API Storage Failure - -**Alert:** `MediaAPIStorageFailure` -**Triggered when:** S3 error rate >10% for 2min -**Impact:** Upload/download failures, broken media references - -### Investigation - -```bash -# Check S3 metrics -curl http://media-api:8080/metrics | grep s3_errors - -# View recent errors -kubectl logs -l app=media-api --tail=50 | grep -i s3 - -# Check AWS status -open https://health.aws.amazon.com/health/status - -# Verify credentials -kubectl get secret media-api-s3-credentials -o yaml -``` - -### Remediation - -1. **Verify S3 bucket exists and is accessible** - ```bash - aws s3 ls s3://jan-media-bucket/ - ``` - -2. **Check IAM permissions** - ```bash - aws iam simulate-principal-policy \ - --policy-source-arn arn:aws:iam::ACCOUNT:role/media-api-role \ - --action-names s3:PutObject s3:GetObject - ``` - -3. **Enable fallback storage** - ```bash - kubectl set env deployment/media-api STORAGE_FALLBACK_ENABLED=true - ``` - ---- - -## 5. Trace Export Failure - -**Alert:** `TraceExportFailure` -**Triggered when:** Jaeger export failing >10 spans/sec for 5min -**Impact:** Partial trace loss, incomplete observability - -### Investigation - -```bash -# Check collector export metrics -curl http://otel-collector:8889/metrics | grep exporter_send_failed - -# Check Jaeger ingestion -curl http://jaeger-collector:14269/metrics | grep spans_received - -# View collector logs -kubectl logs -l app=otel-collector | grep -i error -``` - -### Remediation - -1. **Verify Jaeger collector is running** - ```bash - kubectl get pods -l app=jaeger - kubectl logs -l app=jaeger --tail=50 - ``` - -2. **Check network connectivity** - ```bash - kubectl run -it --rm debug --image=curlimages/curl --restart=Never \ - -- curl -v http://jaeger-collector:14268/api/traces - ``` - -3. **Increase collector retry settings** - - Edit `monitoring/otel-collector.yaml` - - Increase `max_elapsed_time` from 5m to 10m - - Increase `queue_size` from 5000 to 10000 - - Apply config: `kubectl apply -f monitoring/otel-collector.yaml` - -4. **Temporary: Reduce sampling rate** - ```bash - kubectl set env deployment/llm-api OTEL_TRACES_SAMPLER_ARG=0.1 - kubectl set env deployment/response-api OTEL_TRACES_SAMPLER_ARG=0.1 - ``` - ---- - -## 6. Conversation Classifier Errors - -**Alert:** `ConversationInsightFailure` -**Triggered when:** Classifier error rate >5% for 5min -**Impact:** Missing conversation metadata, incomplete analytics - -### Investigation - -```bash -# View classifier metrics -curl http://response-api:8081/metrics | grep classifier_errors - -# Review error logs -kubectl logs -l app=response-api | grep classifier -``` - -### Remediation - -1. **Check for malformed prompt data** - ```bash - # Review recent requests - kubectl logs -l app=response-api --tail=100 | grep -A5 "classifier error" - ``` - -2. **Review recent classifier configuration changes** - ```bash - git log --since="1 day ago" --grep="classifier" --oneline - kubectl describe configmap response-api-config - ``` - -3. **Disable classifier temporarily (if persistent)** - ```bash - kubectl set env deployment/response-api CLASSIFIER_ENABLED=false - ``` - ---- - -## Appendix A: Common Commands - -### Health Checks - -```bash -# All services -make health-check - -# Individual service -curl http://SERVICE:PORT/health - -# Monitoring stack -make monitor-test -``` - -### Viewing Logs - -```bash -# Recent logs -kubectl logs -l app=SERVICE --tail=100 - -# Follow logs -kubectl logs -l app=SERVICE -f - -# Logs with timestamp -kubectl logs -l app=SERVICE --timestamps=true -``` - -### Metrics Queries - -```bash -# Service metrics -curl http://SERVICE:8080/metrics - -# Prometheus query -curl 'http://localhost:9090/api/v1/query?query=METRIC_NAME' - -# Alert status -curl http://localhost:9090/api/v1/rules -``` - -### Trace Queries - -```bash -# Recent traces for service -curl 'http://localhost:16686/api/traces?service=SERVICE&limit=10' - -# Specific trace -curl 'http://localhost:16686/api/traces/TRACE_ID' - -# Slow traces -curl 'http://localhost:16686/api/traces?service=SERVICE&minDuration=2s' -``` - ---- - -## Appendix B: Escalation Contacts - -| Severity | Contact | Response Time | Channel | -|----------|---------|---------------|---------| -| P0 (Critical) | SRE On-Call | <5min | PagerDuty | -| P1 (High) | Team Lead | <15min | Slack #incidents | -| P2 (Medium) | Dev Team | <1h | Slack #engineering | -| P3 (Low) | Ticket Queue | Next business day | Jira | - ---- - -## Appendix C: Useful Links - -- **Grafana:** http://localhost:3000 -- **Jaeger:** http://localhost:16686 -- **Prometheus:** http://localhost:9090 -- **Monitoring Guide:** [docs/guides/monitoring.md](../guides/monitoring) -- **Architecture Overview:** [docs/architecture/services.md](../architecture/services) -- **Security Policy:** [docs/architecture/security.md](../architecture/security) diff --git a/apps/platform/eslint.config.mjs b/apps/platform/eslint.config.mjs deleted file mode 100644 index 35ba220b..00000000 --- a/apps/platform/eslint.config.mjs +++ /dev/null @@ -1,19 +0,0 @@ -import { baseConfig, typescriptConfig } from '../../eslint.base.config.mjs'; - -/** - * ESLint configuration for apps/platform - * This is a Next.js/Node.js platform service - */ -export default [ - ...baseConfig, - { - ...typescriptConfig, - languageOptions: { - ...typescriptConfig.languageOptions, - parserOptions: { - projectService: true, - tsconfigRootDir: import.meta.dirname, - }, - }, - }, -]; diff --git a/apps/platform/next.config.mjs b/apps/platform/next.config.mjs deleted file mode 100644 index 5512e49f..00000000 --- a/apps/platform/next.config.mjs +++ /dev/null @@ -1,23 +0,0 @@ -import { createMDX } from 'fumadocs-mdx/next'; - -const withMDX = createMDX(); - -/** @type {import('next').NextConfig} */ -const config = { - reactStrictMode: true, - serverExternalPackages: ['typescript', 'twoslash'], - redirects: async () => [ - { - source: '/docs', - destination: '/docs/overview', - permanent: true, - }, - { - source: '/docs/api-reference', - destination: '/docs/api-reference/introduction', - permanent: true, - }, - ], -}; - -export default withMDX(config); diff --git a/apps/platform/package.json b/apps/platform/package.json deleted file mode 100644 index 01eaf99c..00000000 --- a/apps/platform/package.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "name": "@janhq/platform", - "version": "0.0.0", - "private": true, - "scripts": { - "build": "next build", - "dev": "next dev --turbopack", - "start": "next start", - "postinstall": "fumadocs-mdx", - "generate-openai": "tsx scripts/generate-fumadocs-openapi.ts", - "generate-openapi": "tsx scripts/generate-fumadocs-openapi.ts", - "lint": "eslint" - }, - "dependencies": { - "@hookform/resolvers": "^5.2.2", - "@radix-ui/react-avatar": "^1.1.11", - "@radix-ui/react-dropdown-menu": "^2.1.16", - "@radix-ui/react-label": "^2.1.8", - "@radix-ui/react-separator": "^1.1.8", - "@tabler/icons-react": "^3.35.0", - "@types/js-yaml": "^4.0.9", - "ajv": "^8.0.0", - "fumadocs-core": "16.0.3", - "fumadocs-mdx": "13.0.2", - "fumadocs-openapi": "^9.5.1", - "fumadocs-twoslash": "^3.1.8", - "fumadocs-ui": "16.0.3", - "input-otp": "^1.4.2", - "js-cookie": "^3.0.5", - "js-yaml": "^4.1.0", - "lucide-react": "0.548.0", - "next": "^16.0.7", - "openapi-snippet": "^0.12.1", - "react": "^19.2.1", - "react-dom": "^19.2.1", - "react-hook-form": "^7.66.1", - "shiki": "^3.13.0", - "zustand": "^5.0.8" - }, - "devDependencies": { - "@eslint/eslintrc": "^3", - "@eslint/js": "^9.39.1", - "@tailwindcss/postcss": "^4.1.14", - "@types/js-cookie": "^3.0.6", - "@types/mdx": "^2.0.13", - "@types/node": "24.6.2", - "@types/react": "^19.2.0", - "@types/react-dom": "^19.2.0", - "@typescript-eslint/eslint-plugin": "^8.46.1", - "@typescript-eslint/parser": "^8.46.1", - "eslint": "^9.37.0", - "eslint-config-next": "16.0.0", - "eslint-config-prettier": "^10.1.8", - "eslint-plugin-mdx": "^3.6.2", - "postcss": "^8.5.6", - "prettier": "^3.6.2", - "prettier-plugin-organize-imports": "^4.3.0", - "prettier-plugin-tailwindcss": "^0.7.0", - "remark-lint": "^10.0.1", - "tailwindcss": "^4.1.14", - "tsx": "^4.20.6", - "typescript": "^5.9.3", - "typescript-eslint": "^8.46.4" - } -} diff --git a/apps/platform/postcss.config.mjs b/apps/platform/postcss.config.mjs deleted file mode 100644 index a34a3d56..00000000 --- a/apps/platform/postcss.config.mjs +++ /dev/null @@ -1,5 +0,0 @@ -export default { - plugins: { - '@tailwindcss/postcss': {}, - }, -}; diff --git a/apps/platform/public/favicon.ico b/apps/platform/public/favicon.ico deleted file mode 100644 index ca006fa7d5ba79977736ddb361fe35840073db41..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15406 zcmeHOeQ;b=6<VIIp>~p?u*TKl`YFwTx>&GZhL5z&322;W-BjGKHJyXY=1!A zo;}9PwAd4~g5*?_FHNeC<3vHCf~u{@peA#-2e?>IFjcJ(IvR|Tg< z;j!T-*#E?d7Yh8{uJ8La*AM-g$6nnheuMT2wg2|+ivrkFb18O@9%cX2El1X!>N<9l zue;^p>VSBVV^{P}h1LE$d#A*vlas0bC7xI(`}5b^>ss@pnpfQck-`2ICBi|-YapBKZw5nDDSyAwoSY63dgAm&CIZ@3`~o4 zEwL!?H?)oQ*y`$1>$t6F;wx)CBewv*V^{Cg4B9=5F<%4U#>!7k_2_-t$H&ULCOQ7a zty;X#_QHQ>)OU%w4V{*0=-1BJ9e@%I1>Qxr;3_}AKx38EJy#h zDtuFKu6K=ZmUF0hTjW-UW_n6bOa!m(iQj-WgBwpwM8S`HaUb5;F@EkR9l<+JYo2{K z1>*N&oeq`v3#v%!xBl38Sy9XII(u;XFzCf0XAf0{rk}(d62;D;TEFK0MX%R&x38;p zd#aOiQ+-yH+55yB8`{VHo4psRFozg;eN@xh?(_GweBGxtKdEUNG&aqzPF%gIBX&o= zb7%|o2ekc#?(lVfZ_AzjZugJs+I+<~qWky`1zbUJ)B$L%4s;qpQ~Gw#hvzavg);wK+x3a&gr7 zMG0&~n@T6sGclC7M@MsC3V&uH^v|7h<^(%kr9#ZwQTF3{8*4ppA2HM44SL(O_#EDF zk1ISH4X<^MWV91~TI;4jxBFP2+jAK9C{N=3vQuQie*GMDGSViFjU5xI{ltuGWBZuM zZ3sUt$5czw_IceW0-F2DzMkf-u&a;xdfcC4J9{W`7siU2$FsWUezvB0MC|rY&zV2l z?)E1{enX^Rw|xq6O>-84o(FS(#@E%h3-`UfUiS}9ood@)_Y3YD?^)4P>$ujdj}6bG zE!!0}kNh+5$nZ{uye)wJ2X?$Gpf#0~Hu(P(<=`VevC+!!kmIQ(=S}*YtNM=5>sw>P z+4X}TRq6Y@uEWIO>vlbb@*rum4fece(C?CKE@_ef1x+KmPa*5fWoi5PM0Wkqttx%c ztPl0LcfePAojjl~Xb*^QnENjD9($}V16&KWPie=P$g4lUPo>Xw2LHe5*Id7*-ofX( z2{z_GvOoL;+SPx{=S(H_FV`6n@Hytu-_$W4&#w=ELZ#1VUr+1p{w~j!8ch^}w(c9z zj;?PTOXM_!kMB5jkv`JAF|F#hb;m_^Lqx!zn$sTiC-WWQy;k&DM?E)uRlOg4CF<-m z6}|E6;77e}Ma!y=a=`f)9EU8L)&caV^BM+kPe&hXz7ON+dcL`HQsg+oZH3Ml?EUm% zNr(P&Vbk!?&eI9q=altUWv(--`{?rBX85{R>o4(u&P!~Iy^DI(uG8+*J?~D ztZ_$cu||KD<64#QZw2Cs7<|T*-P(?I$#D$bE9t2v?0X%2yYu;t(ar+r;HIiGq6%Zb z!|};e$o6EmBl>W4C_!1!F;(~z3CL!oGB`aVaZqjwp@*v+;e%MWPFYXh6*Pt)Rq4v- za!uqF9*s`#y9mvDP&j&udHfXB@> z0dXtpbUt(LYnw;LRyBn0v)k*S&ndrIgWu&IplljX82O{s{-P2wx>NWa*Yn=IisJX zZUTQR>p72-mYB$?kJiv6yGrrCQLQBK?ir!y3ZR+kPjqWj)uU*b@_3 zN6z0*e;YnbE%sDbpVxhzPjjC}oRzg0dTLwPngD4d-cRH=4!=P^PWK0mHPzRnG1|ht z%h%<)9dmy@sJZuYFUOwx9d*3LK7;lk=|kpXkcZE+f7O}9-K^sU8!(t%fBxnqZP(v| zUe6c6`v-vIFTE#Ssd#*`bY*+$@cOoKPj*A}0qMsh{-`7Et=>5FE%GetvyiWOG3agi znBVL9H0IqOLVPU8xZEZ6ZbxszvHB?NnKM!($H%&V(bw%h>F;W`+~;Mxm2!mx%#$ER zYpcbzk|zNZR(>B^4qz`VuMSS{$9}BAE)U4P;o7cAQS2HW$a93B%c~!%wTi#r&q))v zJv8f6O5AEmzoQg!;OfRm6fs2I2Vox7A#snH*D7YKXU+q8dgR2y1KG|f&CmPtW2|F^ zZ=rQKui+dJ&kff8zbAkgWB)>NW@`6-%)#(kb-yhWwuQbc!}$~9bm&C1{7SDwjZf@B zTz1L0-wHeZM5}t%tw$_HeOM}fBX74qptYyvfo>m{hK^$~;E!l3Uh}i$JxdMvk+Y`E%+X!yE9_*y z6^+H#F_%aFZ|_NOKjoOjkG-JZj90w}-sgc=pQA|!_L%XF7R}(M#GO1HX`1tjb=^t?T!!9L?@o>16&= zwomdeUCCVjLe^j*_~o1oIck#6>0}<~Cjz-bp5H7>AIN{s!Ti0AI^TuJkHm{T>JglI z?vc+{GGEdf)3MVgV{P`S^cM5HE14fPborvCEm{nIiJPSrHl7`U@6l}M;GX6B4|1N; zmU1m*ow-~JenW2N_>>0r)L9X1X_J}Ga_&pvFH?@_)2ZWZ!(NBA&Zxg;zU#bC#@nf} za3-S5jGik+1FJolHW6ne`lVu6$TR(+HSp=wwt60V5%^WPp|8TTQ_4H%X0eXZoitQY9N=opxCL;1mZgQoJI&y=5q z#$uAb6?UvmtvbGbu7~j{Tj|z@@&{WpN1x<4(w7GQ0mCNDlc6;@GtP8ID|su+&0X;W zIA_*y*87O81NL$?PUIXZTXKEOYbWt-=opXEX02(8GOscZ8^3KK?^hY$5TMO~+KzEd zPQ%bW63bFbHC`d#3c(j((|rw9Z-@c$EwKxHUr}rIKE-=SB(-Pae0iQ-qYcfg57#XP zH~U!SMv{AQq~FHx4)L>^AJ-<^@O{8t5`H!Ez0Y^Cg-oyG-|H*{9#Rs$tenu%GIma*n#rn&zm=GJR!LXBm43x|1;z^EBq1lf56g zcwCuqW_ri`ApV!2jyBNTj&oCGl;2NKucb_@@+a-(Dw8cV@_fjZCG$%%7Bthk68Gk{ zr^?~%;m$h_H=z|ApLz7jw3OxUYtWm;{Mid9~k-A84- z%A5hmHRhu6{!0E-KcV#Ggzh8A{eZMp-sArq=IE>`w`b&>lDwC4bS3ed{UE8cb8M=j z8`54fZX+%ecH}+U%gs5bD~Vfc07FF_Z)SavZd!p zOgbyaqRarho~sAnKQP!OK731b0Q+oHma=ci_XghwxcADsm2!mxD;!wiz=weY{{uiP Bn->58 diff --git a/apps/platform/public/jan_logo.svg b/apps/platform/public/jan_logo.svg deleted file mode 100644 index 3faa0463..00000000 --- a/apps/platform/public/jan_logo.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/apps/platform/scripts/README.md b/apps/platform/scripts/README.md deleted file mode 100644 index 09b62aed..00000000 --- a/apps/platform/scripts/README.md +++ /dev/null @@ -1,185 +0,0 @@ -# Scripts Directory - -This directory contains utility scripts for the Jan Platform. - -## Environment Setup Scripts - -### setup-env.sh / setup-env.bat - -Interactive scripts to help you quickly set up your `.env.local` file. - -**Usage (Linux/Mac):** - -```bash -./scripts/setup-env.sh -``` - -**Usage (Windows):** - -```cmd -scripts\setup-env.bat -``` - -These scripts will: - -1. Copy `.env.example` to `.env.local` -2. Prompt you for API URL configuration -3. Backup existing `.env.local` if it exists - ---- - -## test-import.ts - -A simple test file to verify fumadocs-openapi import is working correctly. - -**Usage:** - -```bash -npx tsx scripts/test-import.ts -``` - ---- - -## Documentation Generation Scripts - -### generate-fumadocs-openapi.ts - -The main documentation generator that creates interactive API reference pages with live playground components. - -### What it does - -1. **Cleans previous generation** - - Removes all tag subdirectories in `content/docs/api-reference/` - - Preserves root-level manual documentation files (introduction.mdx, meta.json, etc.) - -2. **Generates MDX files** - - Reads OpenAPI schema from `api/server.yaml` - - Creates one MDX file per API operation - - Uses custom slug logic to organize files by tag/resource/method - - Embeds interactive `` components for live API testing - -3. **Auto-generates navigation** - - Extracts tag names from OpenAPI spec - - Creates `meta.json` files for each tag directory - - Sorts endpoints by HTTP method (GET, POST, PATCH, DELETE) then alphabetically - -### Usage - -```bash -npm run generate-openapi -``` - -Or via the full workflow: - -```bash -# Generate OpenAPI spec from backend source -npm run generate-openapi - -# Process MDX and build documentation -npm run postinstall - -# Start dev server -npm run dev -``` - -### File Structure - -``` -content/docs/api-reference/ -├── introduction.mdx # Manual (preserved) -├── debugging-requests.mdx # Manual (preserved) -├── meta.json # Manual (preserved) -├── authentication/ # Auto-generated -│ ├── meta.json # Auto-generated -│ ├── google/ -│ │ ├── login/ -│ │ │ └── get.mdx # Auto-generated -│ │ └── callback/ -│ │ └── post.mdx # Auto-generated -│ └── me/ -│ └── get.mdx # Auto-generated -└── chat-completions/ # Auto-generated - ├── meta.json # Auto-generated - └── completions/ - └── post.mdx # Auto-generated -``` - -### Key Features - -**Zero-maintenance scalability** - -- Add new OpenAPI tags → automatically creates new directories -- Remove tags → automatically cleans up old directories -- No hardcoded tag lists to maintain - -**Smart slug generation** - -- Strips version prefixes (`/v1/auth/login` → `auth/login`) -- Removes trailing path parameters (`/users/{id}` → `/users`) -- Drops redundant tag prefixes from paths -- Converts to lowercase kebab-case - -**Proper tag titles** - -- Reads tag names directly from OpenAPI spec -- Maps slugified directories to human-readable titles -- Example: `admin-provider/` → "Admin Provider" - -### Configuration - -Edit these constants in the script if needed: - -```typescript -const OUTPUT_ROOT = './content/docs/api-reference'; -const VERSION_SEGMENT_REGEX = /^v\d+$/i; -``` - -### How Slug Generation Works - -For an endpoint: `POST /v1/auth/google/callback` - -1. Tag from OpenAPI: `Authentication` → slug: `authentication` -2. Strip version: `/auth/google/callback` -3. Split into segments: `['auth', 'google', 'callback']` -4. Drop tag prefix: `['google', 'callback']` (removes redundant 'auth') -5. Add method: `['authentication', 'google', 'callback', 'post']` -6. Result: `authentication/google/callback/post.mdx` - -### Troubleshooting - -**Missing meta.json files** - -- Ensure the OpenAPI spec has proper tags on all operations -- Check console output for tag extraction results - -**Wrong file paths** - -- Verify OpenAPI paths follow `/v1/{resource}/{action}` pattern -- Check that tag names match expected slugified format - -**Stale documentation** - -- All subdirectories are deleted on each run -- If files persist, they may be in the wrong location (should be in subdirectories, not root) - -## Development - -**Dependencies:** - -- `fumadocs-openapi` - Generates MDX files from OpenAPI -- `js-yaml` - Parses YAML OpenAPI specs -- Node.js `fs` - File system operations - -**Adding new features:** - -1. Update slug logic in `buildOperationSlug()` -2. Modify sorting in `sortPages()` -3. Customize meta.json structure in `generateTagMetaFiles()` - -**Testing changes:** - -```bash -# Clean and regenerate -rm -rf content/docs/api-reference/*/ -npm run generate-openapi -``` diff --git a/apps/platform/scripts/generate-fumadocs-openapi.ts b/apps/platform/scripts/generate-fumadocs-openapi.ts deleted file mode 100644 index 80a6fa99..00000000 --- a/apps/platform/scripts/generate-fumadocs-openapi.ts +++ /dev/null @@ -1,326 +0,0 @@ -/** - * Generates interactive API documentation from OpenAPI spec using Fumadocs. - * - * Process: - * 1. Cleans all generated tag directories in content/docs/api-reference - * - Deletes all subdirectories (generated content) - * - Preserves root-level .mdx files and meta.json (manual documentation) - * 2. Reads OpenAPI schema from api/server.yaml - * 3. Generates one MDX file per API operation with custom slug logic: - * - Strips version segments (e.g., /v1/) - * - Removes trailing path parameters (e.g., {id}) - * - Drops tag prefixes from paths to avoid redundancy - * - Organizes by: {tag}/{resource-path}/{http-method}.mdx - * 4. Creates files with components for interactive API playground - * 5. Auto-generates meta.json files for each tag directory: - * - Scans generated tag directories - * - Discovers all endpoint .mdx files - * - Creates navigation metadata with proper tag titles from OpenAPI spec - * - Sorts pages by HTTP method (GET, POST, PATCH, DELETE) then alphabetically - * - * This approach ensures docs always match the OpenAPI spec with no manual maintenance. - */ -import { load } from 'js-yaml'; -import { existsSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs'; -import path from 'node:path'; - -type GenerateFiles = (typeof import('fumadocs-openapi'))['generateFiles']; - -const loadGenerateFiles = async () => { - const { generateFiles } = await import('fumadocs-openapi'); - return generateFiles as GenerateFiles; -}; - -const OUTPUT_ROOT = './content/docs/api-reference'; -const VERSION_SEGMENT_REGEX = /^v\d+$/i; - -const toPosixPath = (value: string) => value.replace(/\\/g, '/'); - -/** - * Converts a string into a URL-friendly slug - * Removes special characters, replaces spaces with hyphens, and lowercases - */ -const slugifySegment = (value: string) => - value - .trim() - .replace(/[{}]/g, '') - .replace(/[^a-zA-Z0-9]+/g, '-') - .replace(/-+/g, '-') - .replace(/^-|-$/g, '') - .toLowerCase(); - -/** - * Strips version prefix (e.g., /v1/) from API route path and returns path segments - */ -const stripVersionAndParams = (routePath: string) => { - const rawSegments = routePath.split('/').filter(Boolean); - if (rawSegments.length > 0 && VERSION_SEGMENT_REGEX.test(rawSegments[0])) { - rawSegments.shift(); - } - - return rawSegments; -}; - -/** - * Removes tag prefix from path segments to avoid redundancy in URLs - * Example: ['auth', 'google', 'login'] with tag 'auth' -> ['google', 'login'] - */ -const dropTagPrefix = (segments: string[], tagSlug: string) => { - let combined = ''; - let prefixEnd = -1; - - for (let i = 0; i < segments.length; i += 1) { - combined = combined ? `${combined}-${segments[i]}` : segments[i]; - if (combined === tagSlug) { - prefixEnd = i; - } - } - - if (prefixEnd >= 0) { - const sliced = segments.slice(prefixEnd + 1); - if (sliced.length > 0) { - return sliced; - } - } - - if (segments.length > 1 && tagSlug.startsWith(segments[0])) { - return segments.slice(1); - } - - return segments; -}; - -/** - * Builds the file path slug for an API operation - * Combines tag, resource path, and HTTP method into a clean URL structure - * Example: /v1/auth/google/login GET -> authentication/google/login/get - */ -const buildOperationSlug = (entry: any): string => { - if (entry.type !== 'operation') { - return entry.item.method.toLowerCase(); - } - - const tags: string[] = entry.item.tags ?? []; - const tagSlug = slugifySegment(tags[0] ?? 'general') || 'general'; - - const rawSegments = stripVersionAndParams(entry.item.path); - - const cleanedSegments: string[] = rawSegments - .map((segment, index) => { - const isParam = segment.startsWith('{') && segment.endsWith('}'); - const isLast = index === rawSegments.length - 1; - - if (isParam && isLast) { - return undefined; - } - - const slug = slugifySegment(segment); - return slug || undefined; - }) - .filter((segment): segment is string => Boolean(segment)); - - let resourceSegments = cleanedSegments; - - if (resourceSegments.length === 0) { - resourceSegments = ['root']; - } - - resourceSegments = dropTagPrefix(resourceSegments, tagSlug); - - if (resourceSegments.length === 0) { - resourceSegments = cleanedSegments.length > 0 ? [cleanedSegments.at(-1)!] : ['root']; - } - - const methodSlug = slugifySegment(entry.item.method); - const finalSegments = [tagSlug, ...resourceSegments, methodSlug].filter(Boolean); - - return finalSegments.join('/'); -}; - -/** - * Removes all previously generated tag directories to ensure a clean slate - * Deletes all subdirectories in api-reference/ while preserving root-level files - * Root-level files (introduction.mdx, meta.json, etc.) are manual documentation - */ -const cleanGeneratedDirectories = () => { - const outputPath = path.resolve(OUTPUT_ROOT); - - if (!existsSync(outputPath)) { - return; - } - - const entries = readdirSync(outputPath); - - for (const entry of entries) { - const fullPath = path.join(outputPath, entry); - const stat = statSync(fullPath); - - // Delete all subdirectories (generated content) - // Preserve root-level files (manual documentation) - if (stat.isDirectory()) { - rmSync(fullPath, { recursive: true, force: true }); - console.log(` 🗑️ Removed ${entry}/`); - } - } -}; - -/** - * Extract tag names from OpenAPI spec and build slug -> title mapping - */ -const extractTagTitles = (schemaPath: string): Map => { - const yaml = readFileSync(schemaPath, 'utf-8'); - const schema = load(yaml) as any; - const tagMap = new Map(); - - // Extract tags from all operations - if (schema.paths) { - for (const pathObj of Object.values(schema.paths)) { - for (const operation of Object.values(pathObj as any)) { - const op = operation as any; - if (op.tags && Array.isArray(op.tags)) { - for (const tag of op.tags) { - const slug = slugifySegment(tag); - if (slug && !tagMap.has(slug)) { - tagMap.set(slug, tag); - } - } - } - } - } - } - - return tagMap; -}; - -/** - * Recursively find all .mdx files in a directory - */ -const findMdxFiles = (dir: string, baseDir: string): string[] => { - const files: string[] = []; - const entries = readdirSync(dir); - - for (const entry of entries) { - const fullPath = path.join(dir, entry); - const stat = statSync(fullPath); - - if (stat.isDirectory()) { - files.push(...findMdxFiles(fullPath, baseDir)); - } else if (entry.endsWith('.mdx')) { - // Convert to relative path (always POSIX) and remove .mdx extension - const relativePath = path.relative(baseDir, fullPath); - const pagePath = toPosixPath(relativePath).replace(/\.mdx$/, ''); - files.push(pagePath); - } - } - - return files; -}; - -/** - * Sort pages by HTTP method order (GET, POST, PATCH, DELETE) and path - */ -const sortPages = (pages: string[]): string[] => { - const methodOrder = ['get', 'post', 'patch', 'delete', 'put']; - - return pages.sort((a, b) => { - const methodA = a.split('/').pop() || ''; - const methodB = b.split('/').pop() || ''; - - const orderA = methodOrder.indexOf(methodA); - const orderB = methodOrder.indexOf(methodB); - - // If methods are different and both are in the order list, sort by method - if (orderA !== -1 && orderB !== -1 && orderA !== orderB) { - return orderA - orderB; - } - - // Otherwise sort alphabetically - return a.localeCompare(b); - }); -}; - -/** - * Generate meta.json files for each tag directory - */ -const generateTagMetaFiles = (tagTitles: Map) => { - const outputPath = path.resolve(OUTPUT_ROOT); - - // Find all directories in OUTPUT_ROOT (excluding root-level files) - const entries = readdirSync(outputPath); - - for (const entry of entries) { - const fullPath = path.join(outputPath, entry); - const stat = statSync(fullPath); - - if (stat.isDirectory()) { - // Find all .mdx files in this tag directory - const pages = findMdxFiles(fullPath, fullPath); - - if (pages.length > 0) { - // Sort pages - const sortedPages = sortPages(pages); - - // Get tag title (default to capitalized slug if not found) - const tagSlug = entry; - const title = - tagTitles.get(tagSlug) || - tagSlug - .split('-') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); - - // Create meta.json content - const metaContent = { - title, - pages: sortedPages, - }; - - // Write meta.json - const metaPath = path.join(fullPath, 'meta.json'); - writeFileSync(metaPath, JSON.stringify(metaContent, null, 2) + '\n'); - console.log(` ✓ Generated ${entry}/meta.json (${sortedPages.length} pages)`); - } - } - } -}; - -/** - * This script uses Fumadocs' native OpenAPI generator - * to create MDX files with interactive API playground components - */ -async function main() { - console.log('🚀 Generating Fumadocs OpenAPI documentation...\n'); - - try { - const schemaPath = './api/server.yaml'; - - console.log('🧹 Cleaning previously generated directories...'); - cleanGeneratedDirectories(); - - console.log('\n📝 Generating MDX files from OpenAPI schema...'); - const generateFiles = await loadGenerateFiles(); - await generateFiles({ - input: [schemaPath], // Path to your OpenAPI schema - output: OUTPUT_ROOT, - per: 'operation', // Generate one file per API operation - groupBy: 'none', - name: (entry) => buildOperationSlug(entry), - }); - console.log('✅ Interactive API documentation generated'); - - // Extract tag titles and generate meta.json files - console.log('\n📋 Generating meta.json files for tag directories...'); - const tagTitles = extractTagTitles(schemaPath); - console.log('✅ Extracted tag titles from OpenAPI schema'); - generateTagMetaFiles(tagTitles); - console.log('✅ Generated meta.json files for each tag directory'); - console.log( - '\n✨ Complete! Files will use the component for interactive playgrounds', - ); - } catch (error) { - console.error('❌ Error generating OpenAPI docs:', error); - process.exit(1); - } -} - -main(); diff --git a/apps/platform/scripts/setup-env.bat b/apps/platform/scripts/setup-env.bat deleted file mode 100644 index c3699a8b..00000000 --- a/apps/platform/scripts/setup-env.bat +++ /dev/null @@ -1,88 +0,0 @@ -@echo off -REM Jan Platform Web - Environment Setup Script -REM This script helps you quickly set up your .env.local file - -echo. -echo ======================================== -echo Jan Platform Web - Environment Setup -echo ======================================== -echo. - -REM Check if .env.example exists -if not exist ".env.example" ( - echo ERROR: .env.example file not found! - echo Please run this script from the platform-web directory. - pause - exit /b 1 -) - -REM Check if .env.local already exists -if exist ".env.local" ( - echo WARNING: .env.local already exists! - set /p overwrite="Do you want to overwrite it? (y/N): " - if /i not "%overwrite%"=="y" ( - echo Setup cancelled. Existing .env.local kept. - pause - exit /b 0 - ) - echo Backing up existing .env.local to .env.local.backup... - copy /y ".env.local" ".env.local.backup" >nul -) - -REM Copy .env.example to .env.local -echo Creating .env.local from .env.example... -copy /y ".env.example" ".env.local" >nul - -REM Prompt for API URL -echo. -echo ======================================== -echo API Configuration -echo ======================================== -echo Please enter your API base URL -echo Press Enter to use default: http://localhost:8000 -echo. -echo Common options: -echo 1. http://localhost:8000 (Local development) -echo 2. https://api-gateway-dev.jan.ai (Staging/Dev server) -echo 3. Custom URL -echo. -set "api_url=http://localhost:8000" -set /p api_url="Enter API URL [http://localhost:8000]: " - -REM If empty, use default -if "%api_url%"=="" set "api_url=http://localhost:8000" - -REM Remove trailing slash if present -if "%api_url:~-1%"=="/" set "api_url=%api_url:~0,-1%" - -REM Validate URL format -echo %api_url% | findstr /r /c:"^http://" /c:"^https://" >nul -if errorlevel 1 ( - echo ERROR: Invalid URL format. URL must start with http:// or https:// - pause - exit /b 1 -) - -REM Update .env.local with the API URL using PowerShell -powershell -Command "(Get-Content .env.local) -replace 'NEXT_PUBLIC_JAN_BASE_URL=.*', 'NEXT_PUBLIC_JAN_BASE_URL=%api_url%' | Set-Content .env.local" - -echo. -echo ======================================== -echo Environment setup complete! -echo ======================================== -echo. -echo Configuration: -echo API URL: %api_url% -echo File: .env.local -echo. -echo Next steps: -echo 1. Start your backend API server (if running locally) -echo 2. Run: npm run dev -echo 3. Open: http://localhost:3000 -echo. -echo To verify your backend is running: -echo curl %api_url%/healthz -echo. -echo For more information, see ENVIRONMENT_SETUP.md -echo. -pause diff --git a/apps/platform/scripts/setup-env.sh b/apps/platform/scripts/setup-env.sh deleted file mode 100644 index d690c505..00000000 --- a/apps/platform/scripts/setup-env.sh +++ /dev/null @@ -1,94 +0,0 @@ -#!/bin/bash - -# Jan Platform Web - Environment Setup Script -# This script helps you quickly set up your .env.local file - -set -e - -echo "🚀 Jan Platform Web - Environment Setup" -echo "========================================" -echo "" - -# Colors for output -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -RED='\033[0;31m' -NC='\033[0m' # No Color - -# Check if .env.example exists -if [ ! -f ".env.example" ]; then - echo -e "${RED}❌ Error: .env.example file not found!${NC}" - echo "Please run this script from the platform-web directory." - exit 1 -fi - -# Check if .env.local already exists -if [ -f ".env.local" ]; then - echo -e "${YELLOW}⚠️ .env.local already exists!${NC}" - read -p "Do you want to overwrite it? (y/N): " -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - echo "Setup cancelled. Existing .env.local kept." - exit 0 - fi - echo "Backing up existing .env.local to .env.local.backup..." - cp .env.local .env.local.backup -fi - -# Copy .env.example to .env.local -echo "Creating .env.local from .env.example..." -cp .env.example .env.local - -# Prompt for API URL -echo "" -echo "📡 API Configuration" -echo "-------------------" -echo "Please enter your API base URL" -echo "Press Enter to use default: http://localhost:8000" -echo "" -echo "Common options:" -echo " 1. http://localhost:8000 (Local development)" -echo " 2. https://api-gateway-dev.jan.ai (Staging/Dev server)" -echo " 3. Custom URL" -echo "" -read -p "Enter API URL [http://localhost:8000]: " api_url - -# Use default if empty -if [ -z "$api_url" ]; then - api_url="http://localhost:8000" -fi - -# Validate URL format -if [[ ! $api_url =~ ^https?:// ]]; then - echo -e "${RED}❌ Invalid URL format. URL must start with http:// or https://${NC}" - exit 1 -fi - -# Remove trailing slash if present -api_url="${api_url%/}" - -# Update .env.local with the API URL -if [[ "$OSTYPE" == "darwin"* ]]; then - # macOS - sed -i '' "s|NEXT_PUBLIC_JAN_BASE_URL=.*|NEXT_PUBLIC_JAN_BASE_URL=$api_url|" .env.local -else - # Linux and others - sed -i "s|NEXT_PUBLIC_JAN_BASE_URL=.*|NEXT_PUBLIC_JAN_BASE_URL=$api_url|" .env.local -fi - -echo "" -echo -e "${GREEN}✅ Environment setup complete!${NC}" -echo "" -echo "Configuration:" -echo " API URL: $api_url" -echo " File: .env.local" -echo "" -echo "Next steps:" -echo " 1. Start your backend API server (if running locally)" -echo " 2. Run: npm run dev" -echo " 3. Open: http://localhost:3000" -echo "" -echo "To verify your backend is running:" -echo " curl $api_url/healthz" -echo "" -echo -e "${YELLOW}📖 For more information, see ENVIRONMENT_SETUP.md${NC}" diff --git a/apps/platform/scripts/test-import.ts b/apps/platform/scripts/test-import.ts deleted file mode 100644 index a1b90f37..00000000 --- a/apps/platform/scripts/test-import.ts +++ /dev/null @@ -1 +0,0 @@ -console.log('Imported successfully'); diff --git a/apps/platform/source.config.mjs b/apps/platform/source.config.mjs deleted file mode 100644 index db784ee1..00000000 --- a/apps/platform/source.config.mjs +++ /dev/null @@ -1 +0,0 @@ -export { default, docs } from './.source/source.config.mjs'; diff --git a/apps/platform/source.config.ts b/apps/platform/source.config.ts deleted file mode 100644 index 220f9f94..00000000 --- a/apps/platform/source.config.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { defineConfig, defineDocs, frontmatterSchema, metaSchema } from 'fumadocs-mdx/config'; - -import { rehypeCodeDefaultOptions } from 'fumadocs-core/mdx-plugins'; -import { transformerTwoslash } from 'fumadocs-twoslash'; - -// You can customise Zod schemas for frontmatter and `meta.json` here -// see https://fumadocs.dev/docs/mdx/collections -export const docs = defineDocs({ - docs: { - schema: frontmatterSchema, - postprocess: { - includeProcessedMarkdown: true, - }, - }, - meta: { - schema: metaSchema, - }, -}); - -export default defineConfig({ - mdxOptions: { - rehypeCodeOptions: { - themes: { - light: 'github-light', - dark: 'github-dark', - }, - transformers: [...(rehypeCodeDefaultOptions.transformers ?? []), transformerTwoslash()], - }, - }, -}); diff --git a/apps/platform/src/app/(home)/layout.tsx b/apps/platform/src/app/(home)/layout.tsx deleted file mode 100644 index b1bf08ed..00000000 --- a/apps/platform/src/app/(home)/layout.tsx +++ /dev/null @@ -1,6 +0,0 @@ -import { baseOptions } from '@/lib/layout.shared'; -import { HomeLayout } from 'fumadocs-ui/layouts/home'; - -export default function Layout({ children }: LayoutProps<'/'>) { - return {children}; -} diff --git a/apps/platform/src/app/(home)/page.tsx b/apps/platform/src/app/(home)/page.tsx deleted file mode 100644 index c42744e1..00000000 --- a/apps/platform/src/app/(home)/page.tsx +++ /dev/null @@ -1,93 +0,0 @@ -'use client'; - -import { ArrowRight, Box, Code2, Terminal } from 'lucide-react'; -import Link from 'next/link'; - -export default function HomePage() { - return ( -
-
-
-
-

- Jan Platform -

-

- Self-hosted agentic AI platform powered by local models -

-
- - Get Started - - - API Reference - -
-
-
- -
-
- -
- -
-

- Quickstart - -

-

- Get started with Jan Server in minutes -

-
-
- - -
- -
-

- Architecture - -

-

- Detailed system architecture and design decisions -

-
-
- - -
- -
-

- API Reference - -

-

- Explore the API reference documentation. -

-
-
- -
-
-
-
- ); -} diff --git a/apps/platform/src/app/(private)/layout.tsx b/apps/platform/src/app/(private)/layout.tsx deleted file mode 100644 index 0989b293..00000000 --- a/apps/platform/src/app/(private)/layout.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { Navbar } from '@/components/navbar'; - -export default function Layout({ children }: { children: React.ReactNode }) { - return ( - <> - -
-
-
- {children} -
-
-
- - ); -} diff --git a/apps/platform/src/app/admin/layout.tsx b/apps/platform/src/app/admin/layout.tsx deleted file mode 100644 index af5978d5..00000000 --- a/apps/platform/src/app/admin/layout.tsx +++ /dev/null @@ -1,170 +0,0 @@ -'use client'; - -import { useAuth } from '@/components/auth-provider'; -import { createAdminAPIClient } from '@/lib/admin/api'; -import { getSharedAuthService } from '@/lib/auth/service'; -import { Box, FileText, LayoutDashboard, Loader2, Shield, Users, Wrench } from 'lucide-react'; -import Link from 'next/link'; -import { usePathname, useRouter } from 'next/navigation'; -import { useEffect, useState } from 'react'; - -export default function AdminLayout({ children }: { children: React.ReactNode }) { - const { user, isLoading: authLoading } = useAuth(); - const router = useRouter(); - const pathname = usePathname(); - const [isAdmin, setIsAdmin] = useState(null); - const [isChecking, setIsChecking] = useState(true); - - useEffect(() => { - async function checkAdminStatus() { - if (authLoading) return; - - if (!user) { - router.push('/auth/keycloak'); - return; - } - - try { - const authService = getSharedAuthService(); - const token = await authService.getValidAccessToken(); - - if (!token) { - router.push('/auth/keycloak'); - return; - } - - const adminClient = createAdminAPIClient(token); - const adminStatus = await adminClient.checkIsAdmin(); - - setIsAdmin(adminStatus); - - if (!adminStatus) { - router.push('/docs'); - } - } catch (error) { - console.error('Failed to check admin status:', error); - setIsAdmin(false); - router.push('/docs'); - } finally { - setIsChecking(false); - } - } - - checkAdminStatus(); - }, [user, authLoading, router]); - - if (authLoading || isChecking || isAdmin === null) { - return ( -
-
- -

Verifying admin access...

-
-
- ); - } - - if (!isAdmin) { - return null; - } - - const navItems = [ - { - title: 'Overview', - href: '/admin', - icon: LayoutDashboard, - }, - { - title: 'User Management', - href: '/admin/users', - icon: Users, - children: [ - { title: 'Users', href: '/admin/users' }, - { title: 'Feature Flags', href: '/admin/users/feature-flags' }, - ], - }, - { - title: 'Model Management', - href: '/admin/models', - icon: Box, - children: [ - { title: 'Providers', href: '/admin/models/providers' }, - { title: 'Provider Models', href: '/admin/models/provider-models' }, - { title: 'Model Catalogs', href: '/admin/models/catalogs' }, - ], - }, - { - title: 'Prompt Templates', - href: '/admin/prompt-templates', - icon: FileText, - }, - { - title: 'MCP Tools', - href: '/admin/mcp-tools', - icon: Wrench, - }, - ]; - - return ( -
- {/* Admin Sidebar */} - - - {/* Main Content */} -
-
{children}
-
-
- ); -} diff --git a/apps/platform/src/app/admin/mcp-tools/components/mcp-tool-modal.tsx b/apps/platform/src/app/admin/mcp-tools/components/mcp-tool-modal.tsx deleted file mode 100644 index 0cebced7..00000000 --- a/apps/platform/src/app/admin/mcp-tools/components/mcp-tool-modal.tsx +++ /dev/null @@ -1,230 +0,0 @@ -'use client'; - -import { Button } from '@/components/ui/button'; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog'; -import { Input } from '@/components/ui/input'; -import { Label } from '@/components/ui/label'; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; -import type { MCPTool, UpdateMCPToolRequest } from '@/lib/admin/api'; -import { createAdminAPIClient } from '@/lib/admin/api'; -import { getSharedAuthService } from '@/lib/auth/service'; -import { AlertCircle, Loader2 } from 'lucide-react'; -import type { ChangeEvent } from 'react'; -import { useEffect, useState } from 'react'; - -interface MCPToolModalProps { - open: boolean; - onClose: (refresh?: boolean) => void; - tool?: MCPTool | null; -} - -export function MCPToolModal({ open, onClose, tool }: MCPToolModalProps) { - const [loading, setLoading] = useState(false); - const [description, setDescription] = useState(''); - const [category, setCategory] = useState(''); - const [isActive, setIsActive] = useState(true); - const [disallowedKeywords, setDisallowedKeywords] = useState(''); - - const categories = ['search', 'scrape', 'file_search', 'code_execution', 'memory']; - - useEffect(() => { - if (tool) { - setDescription(tool.description || ''); - setCategory(tool.category); - setIsActive(tool.is_active); - setDisallowedKeywords(tool.disallowed_keywords?.join('\n') || ''); - } else { - resetForm(); - } - }, [tool, open]); - - function resetForm() { - setDescription(''); - setCategory(''); - setIsActive(true); - setDisallowedKeywords(''); - } - - async function handleSubmit() { - if (!tool) return; - - // Validation - if (!description.trim()) { - alert('Description is required'); - return; - } - - // Parse disallowed keywords (one per line) - const keywordsArray = disallowedKeywords - .split('\n') - .map((k) => k.trim()) - .filter((k) => k.length > 0); - - // Validate regex patterns - // Note: Go regex supports inline flags like (?i) for case-insensitive matching - // JavaScript RegExp doesn't support these, so we strip them for validation only - const goInlineFlags = /^\(\?[imsU]+\)/; - for (const pattern of keywordsArray) { - try { - // Remove Go-style inline flags for JS validation - const jsPattern = pattern.replace(goInlineFlags, ''); - new RegExp(jsPattern); - } catch (error) { - alert(`Invalid regex pattern: ${pattern}`); - return; - } - } - - setLoading(true); - - try { - const authService = getSharedAuthService(); - const token = await authService.getValidAccessToken(); - - if (!token) throw new Error('No authentication token'); - - const client = createAdminAPIClient(token); - - const updateData: UpdateMCPToolRequest = { - description: description.trim(), - category, - is_active: isActive, - disallowed_keywords: keywordsArray, - }; - - await client.mcpTools.updateMCPTool(tool.public_id, updateData); - alert('Tool updated successfully'); - onClose(true); - } catch (error) { - console.error('Failed to save tool:', error); - alert('Failed to save tool'); - } finally { - setLoading(false); - } - } - - if (!tool) return null; - - return ( - onClose()}> - - - Edit MCP Tool - - Update tool description, category, and keyword filters - - - -
- {/* Read-only fields */} -
-
- - -
-
- - -
-
- - {/* Editable fields */} -
- -