✨ feat: Re-arrange docs as needed
This commit is contained in:
parent
361c9eeff4
commit
48ddc20026
@ -1,58 +1,66 @@
|
|||||||
{
|
{
|
||||||
"index": {
|
"index": {
|
||||||
"type": "page",
|
"type": "page",
|
||||||
"title": "Homepage",
|
"title": "Homepage",
|
||||||
"display": "hidden",
|
"display": "hidden",
|
||||||
"theme": {
|
"theme": {
|
||||||
"layout": "raw"
|
"layout": "raw"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"docs": {
|
"docs": {
|
||||||
"type": "page",
|
"type": "page",
|
||||||
"title": "Docs"
|
"title": "Docs"
|
||||||
},
|
},
|
||||||
"platforms": {
|
"server": {
|
||||||
"type": "page",
|
"type": "page",
|
||||||
"title": "Platforms",
|
"title": "Docs",
|
||||||
"display": "hidden"
|
"display": "hidden"
|
||||||
},
|
},
|
||||||
"integrations": {
|
"platforms": {
|
||||||
"type": "page",
|
"type": "page",
|
||||||
"title": "Integrations",
|
"title": "Platforms",
|
||||||
"display": "hidden"
|
"display": "hidden"
|
||||||
},
|
},
|
||||||
"changelog": {
|
"integrations": {
|
||||||
"type": "page",
|
"type": "page",
|
||||||
"title": "Changelog",
|
"title": "Integrations",
|
||||||
"theme": {
|
"display": "hidden"
|
||||||
"layout": "raw"
|
},
|
||||||
}
|
"api-reference": {
|
||||||
},
|
"type": "page",
|
||||||
"blog": {
|
"title": "API reference"
|
||||||
"type": "page",
|
},
|
||||||
"title": "Blog",
|
"changelog": {
|
||||||
"theme": {
|
"type": "page",
|
||||||
"layout": "raw"
|
"title": "Changelog",
|
||||||
}
|
"theme": {
|
||||||
},
|
"layout": "raw"
|
||||||
"post": {
|
}
|
||||||
"type": "page",
|
},
|
||||||
"title": "Post Categories",
|
"blog": {
|
||||||
"display": "hidden"
|
"type": "page",
|
||||||
},
|
"title": "Blog",
|
||||||
|
"theme": {
|
||||||
"download": {
|
"layout": "raw"
|
||||||
"type": "page",
|
}
|
||||||
"theme": {
|
},
|
||||||
"layout": "raw"
|
"post": {
|
||||||
}
|
"type": "page",
|
||||||
},
|
"title": "Post Categories",
|
||||||
"privacy": {
|
"display": "hidden"
|
||||||
"title": "Privacy",
|
},
|
||||||
"display": "hidden"
|
"download": {
|
||||||
},
|
"type": "page",
|
||||||
"support": {
|
"theme": {
|
||||||
"title": "Support",
|
"layout": "raw"
|
||||||
"display": "hidden"
|
}
|
||||||
}
|
},
|
||||||
|
"privacy": {
|
||||||
|
"title": "Privacy",
|
||||||
|
"display": "hidden"
|
||||||
|
},
|
||||||
|
"support": {
|
||||||
|
"title": "Support",
|
||||||
|
"display": "hidden"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
20
docs/src/pages/api-reference/_meta.json
Normal file
20
docs/src/pages/api-reference/_meta.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"get-started-separator": {
|
||||||
|
"title": "Get started",
|
||||||
|
"type": "separator"
|
||||||
|
},
|
||||||
|
"index": "Overview",
|
||||||
|
"installation": "Installation",
|
||||||
|
"configuration": "Configuration",
|
||||||
|
"core-concepts-separator": {
|
||||||
|
"title": "Core concepts",
|
||||||
|
"type": "separator"
|
||||||
|
},
|
||||||
|
"api-reference": "API Reference",
|
||||||
|
"resource-separator": {
|
||||||
|
"title": "Resources",
|
||||||
|
"type": "separator"
|
||||||
|
},
|
||||||
|
"architecture": "Architecture",
|
||||||
|
"development": "Development"
|
||||||
|
}
|
||||||
378
docs/src/pages/api-reference/api-reference.mdx
Normal file
378
docs/src/pages/api-reference/api-reference.mdx
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
---
|
||||||
|
title: API Reference
|
||||||
|
description: Complete API documentation for Jan Server endpoints and OpenAI compatibility.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Base URL
|
||||||
|
|
||||||
|
All API endpoints are available at the API gateway base URL:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:8080/api/v1
|
||||||
|
```
|
||||||
|
|
||||||
|
The API gateway automatically forwards port 8080 when using the standard deployment scripts.
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
Jan Server supports multiple authentication methods:
|
||||||
|
|
||||||
|
### JWT Token Authentication
|
||||||
|
|
||||||
|
Include JWT token in the Authorization header:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer <jwt_token>" \
|
||||||
|
http://localhost:8080/api/v1/protected-endpoint
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Key Authentication
|
||||||
|
|
||||||
|
Include API key in the Authorization header:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer <api_key>" \
|
||||||
|
http://localhost:8080/api/v1/protected-endpoint
|
||||||
|
```
|
||||||
|
|
||||||
|
## OpenAI-Compatible Endpoints
|
||||||
|
|
||||||
|
Jan Server implements OpenAI-compatible endpoints for seamless integration with existing tools.
|
||||||
|
|
||||||
|
### Chat Completions
|
||||||
|
|
||||||
|
**Endpoint**: `POST /api/v1/chat/completions`
|
||||||
|
|
||||||
|
Standard OpenAI chat completions API for conversational AI.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/api/v1/chat/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer <token>" \
|
||||||
|
-d '{
|
||||||
|
"model": "jan-v1-4b",
|
||||||
|
"messages": [
|
||||||
|
{"role": "user", "content": "Hello, how are you?"}
|
||||||
|
],
|
||||||
|
"max_tokens": 100,
|
||||||
|
"temperature": 0.7
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `model` (string): Model identifier (`jan-v1-4b`)
|
||||||
|
- `messages` (array): Conversation history
|
||||||
|
- `max_tokens` (integer): Maximum response tokens
|
||||||
|
- `temperature` (float): Response randomness (0.0 to 2.0)
|
||||||
|
- `stream` (boolean): Enable streaming responses
|
||||||
|
|
||||||
|
### Model Information
|
||||||
|
|
||||||
|
**Endpoint**: `GET /api/v1/models`
|
||||||
|
|
||||||
|
List available models:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/api/v1/models
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"object": "list",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": "jan-v1-4b",
|
||||||
|
"object": "model",
|
||||||
|
"created": 1234567890,
|
||||||
|
"owned_by": "jan"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Completions (Text Generation)
|
||||||
|
|
||||||
|
**Endpoint**: `POST /api/v1/completions`
|
||||||
|
|
||||||
|
Text completion endpoint:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/api/v1/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer <token>" \
|
||||||
|
-d '{
|
||||||
|
"model": "jan-v1-4b",
|
||||||
|
"prompt": "The meaning of life is",
|
||||||
|
"max_tokens": 50
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authentication Endpoints
|
||||||
|
|
||||||
|
### OAuth2 Google Login
|
||||||
|
|
||||||
|
**Endpoint**: `GET /auth/google`
|
||||||
|
|
||||||
|
Redirects to Google OAuth2 authorization:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/auth/google
|
||||||
|
```
|
||||||
|
|
||||||
|
### OAuth2 Callback
|
||||||
|
|
||||||
|
**Endpoint**: `GET /auth/google/callback`
|
||||||
|
|
||||||
|
Handles OAuth2 callback and issues JWT token:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:8080/auth/google/callback?code=<auth_code>&state=<state>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Token Refresh
|
||||||
|
|
||||||
|
**Endpoint**: `POST /api/v1/auth/refresh`
|
||||||
|
|
||||||
|
Refresh expired JWT tokens:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/api/v1/auth/refresh \
|
||||||
|
-H "Authorization: Bearer <expired_token>"
|
||||||
|
```
|
||||||
|
|
||||||
|
## User Management
|
||||||
|
|
||||||
|
### User Profile
|
||||||
|
|
||||||
|
**Endpoint**: `GET /api/v1/user/profile`
|
||||||
|
|
||||||
|
Get current user profile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer <token>" \
|
||||||
|
http://localhost:8080/api/v1/user/profile
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Keys
|
||||||
|
|
||||||
|
**Endpoint**: `POST /api/v1/user/api-keys`
|
||||||
|
|
||||||
|
Generate new API key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/api/v1/user/api-keys \
|
||||||
|
-H "Authorization: Bearer <token>" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"name": "Development Key",
|
||||||
|
"permissions": ["read", "write"]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conversation Management
|
||||||
|
|
||||||
|
### Create Conversation
|
||||||
|
|
||||||
|
**Endpoint**: `POST /api/v1/conversations`
|
||||||
|
|
||||||
|
Create new conversation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/api/v1/conversations \
|
||||||
|
-H "Authorization: Bearer <token>" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"title": "My Conversation",
|
||||||
|
"model": "jan-v1-4b"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### List Conversations
|
||||||
|
|
||||||
|
**Endpoint**: `GET /api/v1/conversations`
|
||||||
|
|
||||||
|
Get user's conversations:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer <token>" \
|
||||||
|
http://localhost:8080/api/v1/conversations
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get Conversation
|
||||||
|
|
||||||
|
**Endpoint**: `GET /api/v1/conversations/{id}`
|
||||||
|
|
||||||
|
Get specific conversation with message history:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer <token>" \
|
||||||
|
http://localhost:8080/api/v1/conversations/123
|
||||||
|
```
|
||||||
|
|
||||||
|
## Health and Status
|
||||||
|
|
||||||
|
### Health Check
|
||||||
|
|
||||||
|
**Endpoint**: `GET /health`
|
||||||
|
|
||||||
|
Basic health check:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/health
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"timestamp": "2024-01-01T12:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### System Status
|
||||||
|
|
||||||
|
**Endpoint**: `GET /api/v1/status`
|
||||||
|
|
||||||
|
Detailed system status:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer <token>" \
|
||||||
|
http://localhost:8080/api/v1/status
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"api_gateway": "healthy",
|
||||||
|
"inference_model": "healthy",
|
||||||
|
"database": "healthy",
|
||||||
|
"external_apis": {
|
||||||
|
"serper": "healthy"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Responses
|
||||||
|
|
||||||
|
Jan Server returns standard HTTP status codes and JSON error responses:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": {
|
||||||
|
"message": "Invalid request format",
|
||||||
|
"type": "invalid_request_error",
|
||||||
|
"code": "invalid_json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Error Codes
|
||||||
|
|
||||||
|
| Status Code | Description |
|
||||||
|
|-------------|-------------|
|
||||||
|
| `400` | Bad Request - Invalid request format |
|
||||||
|
| `401` | Unauthorized - Invalid or missing authentication |
|
||||||
|
| `403` | Forbidden - Insufficient permissions |
|
||||||
|
| `404` | Not Found - Resource not found |
|
||||||
|
| `429` | Too Many Requests - Rate limit exceeded |
|
||||||
|
| `500` | Internal Server Error - Server error |
|
||||||
|
| `503` | Service Unavailable - Service temporarily unavailable |
|
||||||
|
|
||||||
|
## Interactive Documentation
|
||||||
|
|
||||||
|
Jan Server provides interactive Swagger documentation at:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:8080/api/swagger/index.html#/
|
||||||
|
```
|
||||||
|
|
||||||
|
This interface allows you to:
|
||||||
|
- Browse all available endpoints
|
||||||
|
- Test API calls directly from the browser
|
||||||
|
- View request/response schemas
|
||||||
|
- Generate code samples
|
||||||
|
|
||||||
|
The Swagger documentation is auto-generated from Go code annotations and provides the most up-to-date API reference.
|
||||||
|
|
||||||
|
## Rate Limiting
|
||||||
|
|
||||||
|
API endpoints implement rate limiting to prevent abuse:
|
||||||
|
|
||||||
|
- **Authenticated requests**: 1000 requests per hour per user
|
||||||
|
- **Unauthenticated requests**: 100 requests per hour per IP
|
||||||
|
- **Model inference**: 60 requests per minute per user
|
||||||
|
|
||||||
|
Rate limit headers are included in responses:
|
||||||
|
```
|
||||||
|
X-RateLimit-Limit: 1000
|
||||||
|
X-RateLimit-Remaining: 999
|
||||||
|
X-RateLimit-Reset: 1609459200
|
||||||
|
```
|
||||||
|
|
||||||
|
## SDK and Client Libraries
|
||||||
|
|
||||||
|
### JavaScript/Node.js
|
||||||
|
|
||||||
|
Use the OpenAI JavaScript SDK with Jan Server:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import OpenAI from 'openai';
|
||||||
|
|
||||||
|
const openai = new OpenAI({
|
||||||
|
baseURL: 'http://localhost:8080/api/v1',
|
||||||
|
apiKey: 'your-jwt-token'
|
||||||
|
});
|
||||||
|
|
||||||
|
const completion = await openai.chat.completions.create({
|
||||||
|
model: 'jan-v1-4b',
|
||||||
|
messages: [
|
||||||
|
{ role: 'user', content: 'Hello!' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python
|
||||||
|
|
||||||
|
Use the OpenAI Python SDK:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import openai
|
||||||
|
|
||||||
|
openai.api_base = "http://localhost:8080/api/v1"
|
||||||
|
openai.api_key = "your-jwt-token"
|
||||||
|
|
||||||
|
response = openai.ChatCompletion.create(
|
||||||
|
model="jan-v1-4b",
|
||||||
|
messages=[
|
||||||
|
{"role": "user", "content": "Hello!"}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### cURL Examples
|
||||||
|
|
||||||
|
Complete cURL examples for common operations:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get models
|
||||||
|
curl http://localhost:8080/api/v1/models
|
||||||
|
|
||||||
|
# Chat completion
|
||||||
|
curl -X POST http://localhost:8080/api/v1/chat/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"model": "jan-v1-4b",
|
||||||
|
"messages": [{"role": "user", "content": "Hello"}]
|
||||||
|
}'
|
||||||
|
|
||||||
|
# Streaming chat completion
|
||||||
|
curl -X POST http://localhost:8080/api/v1/chat/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"model": "jan-v1-4b",
|
||||||
|
"messages": [{"role": "user", "content": "Tell me a story"}],
|
||||||
|
"stream": true
|
||||||
|
}' \
|
||||||
|
--no-buffer
|
||||||
|
```
|
||||||
191
docs/src/pages/api-reference/architecture.mdx
Normal file
191
docs/src/pages/api-reference/architecture.mdx
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
---
|
||||||
|
title: Architecture
|
||||||
|
description: Technical architecture and system design of Jan Server components.
|
||||||
|
---
|
||||||
|
|
||||||
|
## System Overview
|
||||||
|
|
||||||
|
Jan Server implements a microservices architecture on Kubernetes with three core components communicating over HTTP and managed by Helm charts.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
Client[Client/Browser] --> Gateway[jan-api-gateway:8080]
|
||||||
|
Gateway --> Model[jan-inference-model:8101]
|
||||||
|
Gateway --> DB[(PostgreSQL:5432)]
|
||||||
|
Gateway --> Serper[Serper API]
|
||||||
|
Gateway --> OAuth[Google OAuth2]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
### API Gateway (`jan-api-gateway`)
|
||||||
|
|
||||||
|
**Technology Stack:**
|
||||||
|
- **Language**: Go 1.24.6
|
||||||
|
- **Framework**: Gin web framework
|
||||||
|
- **ORM**: GORM with PostgreSQL driver
|
||||||
|
- **DI**: Google Wire for dependency injection
|
||||||
|
- **Documentation**: Swagger/OpenAPI auto-generated
|
||||||
|
|
||||||
|
**Responsibilities:**
|
||||||
|
- HTTP request routing and middleware
|
||||||
|
- User authentication via JWT and OAuth2
|
||||||
|
- Database operations and data persistence
|
||||||
|
- External API integration (Serper, Google OAuth)
|
||||||
|
- OpenAI-compatible API endpoints
|
||||||
|
- Request forwarding to inference service
|
||||||
|
|
||||||
|
**Key Directories:**
|
||||||
|
```
|
||||||
|
application/
|
||||||
|
├── cmd/server/ # Main entry point and DI wiring
|
||||||
|
├── app/ # Core business logic
|
||||||
|
├── config/ # Environment variables and settings
|
||||||
|
└── docs/ # Auto-generated Swagger docs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inference Model (`jan-inference-model`)
|
||||||
|
|
||||||
|
**Technology Stack:**
|
||||||
|
- **Base Image**: VLLM OpenAI v0.10.0
|
||||||
|
- **Model**: Jan-v1-4B (downloaded from Hugging Face)
|
||||||
|
- **Protocol**: OpenAI-compatible HTTP API
|
||||||
|
- **Features**: Tool calling, reasoning parsing
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- **Model Path**: `/models/Jan-v1-4B`
|
||||||
|
- **Served Name**: `jan-v1-4b`
|
||||||
|
- **Port**: 8101
|
||||||
|
- **Batch Tokens**: 1024 max
|
||||||
|
- **Tool Parser**: Hermes
|
||||||
|
- **Reasoning Parser**: Qwen3
|
||||||
|
|
||||||
|
**Capabilities:**
|
||||||
|
- Text generation and completion
|
||||||
|
- Tool calling and function execution
|
||||||
|
- Multi-turn conversations
|
||||||
|
- Reasoning and chain-of-thought
|
||||||
|
|
||||||
|
### Database (PostgreSQL)
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- **Database**: `jan`
|
||||||
|
- **User**: `jan-user`
|
||||||
|
- **Password**: `jan-password`
|
||||||
|
- **Port**: 5432
|
||||||
|
|
||||||
|
**Schema:**
|
||||||
|
- User accounts and authentication
|
||||||
|
- Conversation history
|
||||||
|
- Project and organization management
|
||||||
|
- API keys and access control
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
### Request Processing
|
||||||
|
|
||||||
|
1. **Client Request**: HTTP request to API gateway on port 8080
|
||||||
|
2. **Authentication**: JWT token validation or OAuth2 flow
|
||||||
|
3. **Request Routing**: Gateway routes to appropriate handler
|
||||||
|
4. **Database Operations**: GORM queries for user data/state
|
||||||
|
5. **Inference Call**: HTTP request to model service on port 8101
|
||||||
|
6. **Response Assembly**: Gateway combines results and returns to client
|
||||||
|
|
||||||
|
### Authentication Flow
|
||||||
|
|
||||||
|
**JWT Authentication:**
|
||||||
|
1. User provides credentials
|
||||||
|
2. Gateway validates against database
|
||||||
|
3. JWT token issued with HMAC-SHA256 signing
|
||||||
|
4. Subsequent requests include JWT in Authorization header
|
||||||
|
|
||||||
|
**OAuth2 Flow:**
|
||||||
|
1. Client redirected to Google OAuth2
|
||||||
|
2. Authorization code returned to redirect URL
|
||||||
|
3. Gateway exchanges code for access token
|
||||||
|
4. User profile retrieved from Google
|
||||||
|
5. Local JWT token issued
|
||||||
|
|
||||||
|
## Deployment Architecture
|
||||||
|
|
||||||
|
### Kubernetes Resources
|
||||||
|
|
||||||
|
**Deployments:**
|
||||||
|
- `jan-api-gateway`: Single replica Go application
|
||||||
|
- `jan-inference-model`: Single replica VLLM server
|
||||||
|
- `postgresql`: StatefulSet with persistent storage
|
||||||
|
|
||||||
|
**Services:**
|
||||||
|
- `jan-api-gateway`: ClusterIP exposing port 8080
|
||||||
|
- `jan-inference-model`: ClusterIP exposing port 8101
|
||||||
|
- `postgresql`: ClusterIP exposing port 5432
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- Environment variables via Helm values
|
||||||
|
- Secrets for sensitive data (JWT keys, OAuth credentials)
|
||||||
|
- ConfigMaps for application settings
|
||||||
|
|
||||||
|
### Helm Chart Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
charts/
|
||||||
|
├── umbrella-chart/ # Main deployment chart
|
||||||
|
│ ├── Chart.yaml
|
||||||
|
│ ├── values.yaml # Configuration values
|
||||||
|
│ └── Chart.lock
|
||||||
|
└── apps-charts/ # Individual service charts
|
||||||
|
├── jan-api-gateway/
|
||||||
|
└── jan-inference-model/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Architecture
|
||||||
|
|
||||||
|
### Authentication Methods
|
||||||
|
- **JWT Tokens**: HMAC-SHA256 signed tokens for API access
|
||||||
|
- **OAuth2**: Google OAuth2 integration for user login
|
||||||
|
- **API Keys**: HMAC-SHA256 signed keys for service access
|
||||||
|
|
||||||
|
### Network Security
|
||||||
|
- **Internal Communication**: Services communicate over Kubernetes cluster network
|
||||||
|
- **External Access**: Only API gateway exposed via port forwarding or ingress
|
||||||
|
- **Database Access**: PostgreSQL accessible only within cluster
|
||||||
|
|
||||||
|
### Data Security
|
||||||
|
- **Secrets Management**: Kubernetes secrets for sensitive configuration
|
||||||
|
- **Environment Variables**: Non-sensitive config via environment variables
|
||||||
|
- **Database Encryption**: Standard PostgreSQL encryption at rest
|
||||||
|
|
||||||
|
Production deployments should implement additional security measures including TLS termination, network policies, and secret rotation.
|
||||||
|
|
||||||
|
## Scalability Considerations
|
||||||
|
|
||||||
|
**Current Limitations:**
|
||||||
|
- Single replica deployments
|
||||||
|
- No horizontal pod autoscaling
|
||||||
|
- Local storage for database
|
||||||
|
|
||||||
|
**Future Enhancements:**
|
||||||
|
- Multi-replica API gateway with load balancing
|
||||||
|
- Horizontal pod autoscaling based on CPU/memory
|
||||||
|
- External database with clustering
|
||||||
|
- Redis caching layer
|
||||||
|
- Message queue for async processing
|
||||||
|
|
||||||
|
## Development Architecture
|
||||||
|
|
||||||
|
### Code Generation
|
||||||
|
- **Swagger**: API documentation generated from Go annotations
|
||||||
|
- **Wire**: Dependency injection code generated from providers
|
||||||
|
- **GORM Gen**: Database model generation from schema
|
||||||
|
|
||||||
|
### Build Process
|
||||||
|
1. **API Gateway**: Multi-stage Docker build with Go compilation
|
||||||
|
2. **Inference Model**: Base VLLM image with model download
|
||||||
|
3. **Helm Charts**: Dependency management and templating
|
||||||
|
4. **Documentation**: Auto-generation during development
|
||||||
|
|
||||||
|
### Local Development
|
||||||
|
- **Hot Reload**: Source code changes reflected without full rebuild
|
||||||
|
- **Database Migrations**: Automated schema updates
|
||||||
|
- **API Testing**: Swagger UI for interactive testing
|
||||||
|
- **Logging**: Structured logging with configurable levels
|
||||||
263
docs/src/pages/api-reference/configuration.mdx
Normal file
263
docs/src/pages/api-reference/configuration.mdx
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
---
|
||||||
|
title: Configuration
|
||||||
|
description: Configure Jan Server environment variables, authentication, and external integrations.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
Jan Server configuration is managed through environment variables defined in the Helm values file at `charts/umbrella-chart/values.yaml`.
|
||||||
|
|
||||||
|
### API Gateway Configuration
|
||||||
|
|
||||||
|
#### Core Settings
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `JAN_INFERENCE_MODEL_URL` | `http://jan-server-jan-inference-model:8101` | Internal URL for inference service |
|
||||||
|
|
||||||
|
#### Authentication
|
||||||
|
|
||||||
|
| Variable | Purpose | Format |
|
||||||
|
|----------|---------|--------|
|
||||||
|
| `JWT_SECRET` | JWT token signing | Base64 encoded HMAC-SHA256 key |
|
||||||
|
| `APIKEY_SECRET` | API key signing | Base64 encoded HMAC-SHA256 key |
|
||||||
|
|
||||||
|
The default JWT and API key secrets are for development only. Generate new secrets for production deployments.
|
||||||
|
|
||||||
|
#### OAuth2 Integration
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `OAUTH2_GOOGLE_CLIENT_ID` | Google OAuth2 application client ID |
|
||||||
|
| `OAUTH2_GOOGLE_CLIENT_SECRET` | Google OAuth2 application secret |
|
||||||
|
| `OAUTH2_GOOGLE_REDIRECT_URL` | Callback URL for OAuth2 flow |
|
||||||
|
|
||||||
|
#### External APIs
|
||||||
|
|
||||||
|
| Variable | Provider | Purpose |
|
||||||
|
|----------|----------|---------|
|
||||||
|
| `SERPER_API_KEY` | Serper | Web search integration |
|
||||||
|
|
||||||
|
#### Database Connection
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `DB_POSTGRESQL_WRITE_DSN` | `host=jan-server-postgresql user=jan-user password=jan-password dbname=jan port=5432 sslmode=disable` | Write database connection |
|
||||||
|
| `DB_POSTGRESQL_READ1_DSN` | `host=jan-server-postgresql user=jan-user password=jan-password dbname=jan port=5432 sslmode=disable` | Read database connection |
|
||||||
|
|
||||||
|
## Helm Configuration
|
||||||
|
|
||||||
|
### Updating Values
|
||||||
|
|
||||||
|
Edit the configuration in `charts/umbrella-chart/values.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jan-api-gateway:
|
||||||
|
env:
|
||||||
|
- name: SERPER_API_KEY
|
||||||
|
value: your_serper_api_key
|
||||||
|
- name: OAUTH2_GOOGLE_CLIENT_ID
|
||||||
|
value: your_google_client_id
|
||||||
|
- name: OAUTH2_GOOGLE_CLIENT_SECRET
|
||||||
|
value: your_google_client_secret
|
||||||
|
```
|
||||||
|
|
||||||
|
### Applying Changes
|
||||||
|
|
||||||
|
After modifying values, redeploy the application:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm upgrade jan-server ./charts/umbrella-chart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authentication Setup
|
||||||
|
|
||||||
|
### JWT Tokens
|
||||||
|
|
||||||
|
Generate a secure JWT signing key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate 256-bit key for HMAC-SHA256
|
||||||
|
openssl rand -base64 32
|
||||||
|
```
|
||||||
|
|
||||||
|
Update the `JWT_SECRET` value in your Helm configuration.
|
||||||
|
|
||||||
|
### API Keys
|
||||||
|
|
||||||
|
Generate a secure API key signing secret:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate 256-bit key for HMAC-SHA256
|
||||||
|
openssl rand -base64 32
|
||||||
|
```
|
||||||
|
|
||||||
|
Update the `APIKEY_SECRET` value in your Helm configuration.
|
||||||
|
|
||||||
|
### Google OAuth2
|
||||||
|
|
||||||
|
1. **Create Google Cloud Project**
|
||||||
|
- Go to [Google Cloud Console](https://console.cloud.google.com)
|
||||||
|
- Create a new project or select existing
|
||||||
|
|
||||||
|
2. **Enable OAuth2**
|
||||||
|
- Navigate to "APIs & Services" > "Credentials"
|
||||||
|
- Create OAuth2 client ID credentials
|
||||||
|
- Set application type to "Web application"
|
||||||
|
|
||||||
|
3. **Configure Redirect URI**
|
||||||
|
```
|
||||||
|
http://localhost:8080/auth/google/callback
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Update Configuration**
|
||||||
|
- Set `OAUTH2_GOOGLE_CLIENT_ID` to your client ID
|
||||||
|
- Set `OAUTH2_GOOGLE_CLIENT_SECRET` to your client secret
|
||||||
|
- Set `OAUTH2_GOOGLE_REDIRECT_URL` to your callback URL
|
||||||
|
|
||||||
|
## External Integrations
|
||||||
|
|
||||||
|
### Serper API
|
||||||
|
|
||||||
|
Jan Server integrates with Serper for web search capabilities.
|
||||||
|
|
||||||
|
1. **Get API Key**
|
||||||
|
- Register at [serper.dev](https://serper.dev)
|
||||||
|
- Generate API key from dashboard
|
||||||
|
|
||||||
|
2. **Configure**
|
||||||
|
- Set `SERPER_API_KEY` in Helm values
|
||||||
|
- Redeploy the application
|
||||||
|
|
||||||
|
### Adding New Integrations
|
||||||
|
|
||||||
|
To add new external API integrations:
|
||||||
|
|
||||||
|
1. **Update Helm Values**
|
||||||
|
```yaml
|
||||||
|
jan-api-gateway:
|
||||||
|
env:
|
||||||
|
- name: YOUR_API_KEY
|
||||||
|
value: your_api_key_value
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Update Go Configuration**
|
||||||
|
|
||||||
|
Add to `config/environment_variables/env.go`:
|
||||||
|
```go
|
||||||
|
YourAPIKey string `env:"YOUR_API_KEY"`
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Redeploy**
|
||||||
|
```bash
|
||||||
|
helm upgrade jan-server ./charts/umbrella-chart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database Configuration
|
||||||
|
|
||||||
|
### Connection Settings
|
||||||
|
|
||||||
|
The default PostgreSQL configuration uses:
|
||||||
|
- **Host**: `jan-server-postgresql` (Kubernetes service name)
|
||||||
|
- **Database**: `jan`
|
||||||
|
- **User**: `jan-user`
|
||||||
|
- **Password**: `jan-password`
|
||||||
|
- **Port**: `5432`
|
||||||
|
- **SSL**: Disabled (development only)
|
||||||
|
|
||||||
|
### Production Database
|
||||||
|
|
||||||
|
For production deployments:
|
||||||
|
|
||||||
|
1. **External Database**
|
||||||
|
- Use managed PostgreSQL service (AWS RDS, Google Cloud SQL)
|
||||||
|
- Update DSN variables with external connection details
|
||||||
|
|
||||||
|
2. **SSL/TLS**
|
||||||
|
- Enable `sslmode=require` in connection strings
|
||||||
|
- Configure certificate validation
|
||||||
|
|
||||||
|
3. **Connection Pooling**
|
||||||
|
- Consider using connection pooler (PgBouncer, pgpool-II)
|
||||||
|
- Configure appropriate pool sizes
|
||||||
|
|
||||||
|
## Model Configuration
|
||||||
|
|
||||||
|
The inference model service is configured via Docker CMD parameters:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
CMD ["--model", "/models/Jan-v1-4B", \
|
||||||
|
"--served-model-name", "jan-v1-4b", \
|
||||||
|
"--host", "0.0.0.0", \
|
||||||
|
"--port", "8101", \
|
||||||
|
"--max-num-batched-tokens", "1024", \
|
||||||
|
"--enable-auto-tool-choice", \
|
||||||
|
"--tool-call-parser", "hermes", \
|
||||||
|
"--reasoning-parser", "qwen3"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Model Parameters
|
||||||
|
|
||||||
|
| Parameter | Value | Description |
|
||||||
|
|-----------|-------|-------------|
|
||||||
|
| `--model` | `/models/Jan-v1-4B` | Path to model files |
|
||||||
|
| `--served-model-name` | `jan-v1-4b` | API model identifier |
|
||||||
|
| `--max-num-batched-tokens` | `1024` | Maximum tokens per batch |
|
||||||
|
| `--tool-call-parser` | `hermes` | Tool calling format |
|
||||||
|
| `--reasoning-parser` | `qwen3` | Reasoning output format |
|
||||||
|
|
||||||
|
Model configuration changes require rebuilding the inference Docker image. This will be configurable via environment variables in future releases.
|
||||||
|
|
||||||
|
## Resource Configuration
|
||||||
|
|
||||||
|
### Kubernetes Resources
|
||||||
|
|
||||||
|
Current deployments use default resource limits. For production:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jan-api-gateway:
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
|
||||||
|
jan-inference-model:
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 1000m
|
||||||
|
memory: 4Gi
|
||||||
|
limits:
|
||||||
|
cpu: 4000m
|
||||||
|
memory: 8Gi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Storage
|
||||||
|
|
||||||
|
PostgreSQL uses default Kubernetes storage. For production:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
postgresql:
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
size: 20Gi
|
||||||
|
storageClass: fast-ssd
|
||||||
|
```
|
||||||
|
|
||||||
|
## Logging Configuration
|
||||||
|
|
||||||
|
Configure logging levels via environment variables:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jan-api-gateway:
|
||||||
|
env:
|
||||||
|
- name: LOG_LEVEL
|
||||||
|
value: info
|
||||||
|
- name: LOG_FORMAT
|
||||||
|
value: json
|
||||||
|
```
|
||||||
|
|
||||||
|
Available log levels: `debug`, `info`, `warn`, `error`
|
||||||
|
Available formats: `text`, `json`
|
||||||
445
docs/src/pages/api-reference/development.mdx
Normal file
445
docs/src/pages/api-reference/development.mdx
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
---
|
||||||
|
title: Development
|
||||||
|
description: Development setup, workflow, and contribution guidelines for Jan Server.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- **Go**: 1.24.6 or later
|
||||||
|
- **Docker**: For containerization
|
||||||
|
- **minikube**: Local Kubernetes development
|
||||||
|
- **Helm**: Package management
|
||||||
|
- **Make**: Build automation
|
||||||
|
|
||||||
|
### Initial Setup
|
||||||
|
|
||||||
|
|
||||||
|
1. **Clone Repository**
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/menloresearch/jan-server
|
||||||
|
cd jan-server
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Install Development Tools**
|
||||||
|
```bash
|
||||||
|
cd apps/jan-api-gateway/application
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Generate Code**
|
||||||
|
```bash
|
||||||
|
make setup
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Start Development Environment**
|
||||||
|
```bash
|
||||||
|
# From project root
|
||||||
|
./scripts/run.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Gateway Development
|
||||||
|
|
||||||
|
### Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
apps/jan-api-gateway/application/
|
||||||
|
├── cmd/server/ # Entry point and dependency injection
|
||||||
|
│ ├── server.go # Main server setup
|
||||||
|
│ ├── wire.go # DI configuration
|
||||||
|
│ └── wire_gen.go # Generated DI code
|
||||||
|
├── app/ # Core application logic
|
||||||
|
│ ├── domain/ # Business entities
|
||||||
|
│ ├── repository/ # Data access layer
|
||||||
|
│ ├── service/ # Business logic
|
||||||
|
│ └── handler/ # HTTP handlers
|
||||||
|
├── config/ # Configuration management
|
||||||
|
└── docs/ # Generated API documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install development dependencies
|
||||||
|
make install
|
||||||
|
|
||||||
|
# Generate API documentation
|
||||||
|
make doc
|
||||||
|
|
||||||
|
# Generate dependency injection code
|
||||||
|
make wire
|
||||||
|
|
||||||
|
# Complete setup (doc + wire)
|
||||||
|
make setup
|
||||||
|
|
||||||
|
# Build application
|
||||||
|
go build -o jan-api-gateway ./cmd/server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Generation
|
||||||
|
|
||||||
|
Jan Server uses code generation for several components:
|
||||||
|
|
||||||
|
**Swagger Documentation:**
|
||||||
|
```bash
|
||||||
|
# Generates docs/swagger.json and docs/swagger.yaml
|
||||||
|
swag init --parseDependency -g cmd/server/server.go -o docs
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dependency Injection:**
|
||||||
|
```bash
|
||||||
|
# Generates wire_gen.go from wire.go providers
|
||||||
|
wire ./cmd/server
|
||||||
|
```
|
||||||
|
|
||||||
|
**Database Models:**
|
||||||
|
```bash
|
||||||
|
# Generate GORM models (when schema changes)
|
||||||
|
go run cmd/codegen/gorm/gorm.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local Development
|
||||||
|
|
||||||
|
#### Running API Gateway Locally
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd apps/jan-api-gateway/application
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
export JAN_INFERENCE_MODEL_URL=http://localhost:8101
|
||||||
|
export JWT_SECRET=your-jwt-secret
|
||||||
|
export DB_POSTGRESQL_WRITE_DSN="host=localhost user=jan-user password=jan-password dbname=jan port=5432 sslmode=disable"
|
||||||
|
|
||||||
|
# Run the server
|
||||||
|
go run ./cmd/server
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Database Setup
|
||||||
|
|
||||||
|
For local development, you can run PostgreSQL directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Using Docker
|
||||||
|
docker run -d \
|
||||||
|
--name jan-postgres \
|
||||||
|
-e POSTGRES_DB=jan \
|
||||||
|
-e POSTGRES_USER=jan-user \
|
||||||
|
-e POSTGRES_PASSWORD=jan-password \
|
||||||
|
-p 5432:5432 \
|
||||||
|
postgres:14
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all tests
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
# Run tests with coverage
|
||||||
|
go test -cover ./...
|
||||||
|
|
||||||
|
# Run specific test package
|
||||||
|
go test ./app/service/...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
app/
|
||||||
|
├── service/
|
||||||
|
│ ├── auth_service.go
|
||||||
|
│ ├── auth_service_test.go
|
||||||
|
│ ├── conversation_service.go
|
||||||
|
│ └── conversation_service_test.go
|
||||||
|
└── handler/
|
||||||
|
├── auth_handler.go
|
||||||
|
├── auth_handler_test.go
|
||||||
|
├── chat_handler.go
|
||||||
|
└── chat_handler_test.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Writing Tests
|
||||||
|
|
||||||
|
Example service test:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func TestAuthService_ValidateToken(t *testing.T) {
|
||||||
|
// Setup
|
||||||
|
service := NewAuthService(mockRepo, mockConfig)
|
||||||
|
|
||||||
|
// Test cases
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
token string
|
||||||
|
expectValid bool
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{"valid token", "valid.jwt.token", true, false},
|
||||||
|
{"invalid token", "invalid.token", false, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
valid, err := service.ValidateToken(tt.token)
|
||||||
|
assert.Equal(t, tt.expectValid, valid)
|
||||||
|
assert.Equal(t, tt.expectError, err != nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker Development
|
||||||
|
|
||||||
|
### Building Images
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build API gateway
|
||||||
|
docker build -t jan-api-gateway:dev ./apps/jan-api-gateway
|
||||||
|
|
||||||
|
# Build inference model
|
||||||
|
docker build -t jan-inference-model:dev ./apps/jan-inference-model
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development Compose
|
||||||
|
|
||||||
|
For local development without Kubernetes:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.dev.yml
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:14
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: jan
|
||||||
|
POSTGRES_USER: jan-user
|
||||||
|
POSTGRES_PASSWORD: jan-password
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
|
||||||
|
api-gateway:
|
||||||
|
build: ./apps/jan-api-gateway
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
environment:
|
||||||
|
- JAN_INFERENCE_MODEL_URL=http://inference-model:8101
|
||||||
|
- DB_POSTGRESQL_WRITE_DSN=host=postgres user=jan-user password=jan-password dbname=jan port=5432 sslmode=disable
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
|
||||||
|
inference-model:
|
||||||
|
build: ./apps/jan-inference-model
|
||||||
|
ports:
|
||||||
|
- "8101:8101"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
### Go Debugging
|
||||||
|
|
||||||
|
For VS Code debugging, add to `.vscode/launch.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Launch Jan API Gateway",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"program": "${workspaceFolder}/apps/jan-api-gateway/application/cmd/server",
|
||||||
|
"env": {
|
||||||
|
"JAN_INFERENCE_MODEL_URL": "http://localhost:8101",
|
||||||
|
"JWT_SECRET": "development-secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Application Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View API gateway logs
|
||||||
|
kubectl logs deployment/jan-server-jan-api-gateway -f
|
||||||
|
|
||||||
|
# View inference model logs
|
||||||
|
kubectl logs deployment/jan-server-jan-inference-model -f
|
||||||
|
|
||||||
|
# View PostgreSQL logs
|
||||||
|
kubectl logs statefulset/jan-server-postgresql -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Levels
|
||||||
|
|
||||||
|
Set log level via environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_LEVEL=debug # debug, info, warn, error
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Style and Standards
|
||||||
|
|
||||||
|
### Go Standards
|
||||||
|
|
||||||
|
- Follow [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments)
|
||||||
|
- Use `gofmt` for formatting
|
||||||
|
- Run `go vet` for static analysis
|
||||||
|
- Use meaningful variable and function names
|
||||||
|
|
||||||
|
### API Standards
|
||||||
|
|
||||||
|
- RESTful endpoint design
|
||||||
|
- OpenAPI/Swagger annotations for all endpoints
|
||||||
|
- Consistent error response format
|
||||||
|
- Proper HTTP status codes
|
||||||
|
|
||||||
|
### Git Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create feature branch
|
||||||
|
git checkout -b feature/your-feature-name
|
||||||
|
|
||||||
|
# Make changes and commit
|
||||||
|
git add .
|
||||||
|
git commit -m "feat: add new authentication endpoint"
|
||||||
|
|
||||||
|
# Push and create PR
|
||||||
|
git push origin feature/your-feature-name
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commit Message Format
|
||||||
|
|
||||||
|
Follow conventional commits:
|
||||||
|
|
||||||
|
```
|
||||||
|
feat: add new feature
|
||||||
|
fix: resolve bug in authentication
|
||||||
|
docs: update API documentation
|
||||||
|
test: add unit tests for service layer
|
||||||
|
refactor: improve error handling
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Testing
|
||||||
|
|
||||||
|
### Load Testing
|
||||||
|
|
||||||
|
Use [k6](https://k6.io) for API load testing:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// load-test.js
|
||||||
|
import http from 'k6/http';
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const response = http.post('http://localhost:8080/api/v1/chat/completions', {
|
||||||
|
model: 'jan-v1-4b',
|
||||||
|
messages: [
|
||||||
|
{ role: 'user', content: 'Hello!' }
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': 'Bearer your-token'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
check(response, {
|
||||||
|
'status is 200': (r) => r.status === 200,
|
||||||
|
'response time < 5000ms': (r) => r.timings.duration < 5000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Run load test:
|
||||||
|
```bash
|
||||||
|
k6 run --vus 10 --duration 30s load-test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memory Profiling
|
||||||
|
|
||||||
|
Enable Go profiling endpoints:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import _ "net/http/pprof"
|
||||||
|
|
||||||
|
// In main.go
|
||||||
|
go func() {
|
||||||
|
log.Println(http.ListenAndServe("localhost:6060", nil))
|
||||||
|
}()
|
||||||
|
```
|
||||||
|
|
||||||
|
Profile memory usage:
|
||||||
|
```bash
|
||||||
|
go tool pprof http://localhost:6060/debug/pprof/heap
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
### Pull Request Process
|
||||||
|
|
||||||
|
1. **Fork the repository**
|
||||||
|
2. **Create feature branch** from `main`
|
||||||
|
3. **Make changes** following code standards
|
||||||
|
4. **Add tests** for new functionality
|
||||||
|
5. **Update documentation** if needed
|
||||||
|
6. **Submit pull request** with clear description
|
||||||
|
|
||||||
|
### Code Review Checklist
|
||||||
|
|
||||||
|
- [ ] Code follows Go standards
|
||||||
|
- [ ] Tests added for new features
|
||||||
|
- [ ] Documentation updated
|
||||||
|
- [ ] API endpoints have Swagger annotations
|
||||||
|
- [ ] No breaking changes without version bump
|
||||||
|
- [ ] Security considerations addressed
|
||||||
|
|
||||||
|
### Issues and Bug Reports
|
||||||
|
|
||||||
|
When reporting bugs, include:
|
||||||
|
|
||||||
|
- **Environment**: OS, Go version, minikube version
|
||||||
|
- **Steps to reproduce**: Clear, minimal reproduction steps
|
||||||
|
- **Expected behavior**: What should happen
|
||||||
|
- **Actual behavior**: What actually happens
|
||||||
|
- **Logs**: Relevant error messages or logs
|
||||||
|
|
||||||
|
For security issues, please report privately to the maintainers instead of creating public issues.
|
||||||
|
|
||||||
|
## Release Process
|
||||||
|
|
||||||
|
### Version Management
|
||||||
|
|
||||||
|
Jan Server uses semantic versioning (semver):
|
||||||
|
|
||||||
|
- **Major**: Breaking changes
|
||||||
|
- **Minor**: New features, backward compatible
|
||||||
|
- **Patch**: Bug fixes, backward compatible
|
||||||
|
|
||||||
|
### Building Releases
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Tag release
|
||||||
|
git tag -a v1.2.3 -m "Release v1.2.3"
|
||||||
|
|
||||||
|
# Build release images
|
||||||
|
docker build -t jan-api-gateway:v1.2.3 ./apps/jan-api-gateway
|
||||||
|
docker build -t jan-inference-model:v1.2.3 ./apps/jan-inference-model
|
||||||
|
|
||||||
|
# Push tags
|
||||||
|
git push origin v1.2.3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deployment
|
||||||
|
|
||||||
|
Production deployments follow the same Helm chart structure:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deploy specific version
|
||||||
|
helm install jan-server ./charts/umbrella-chart \
|
||||||
|
--set jan-api-gateway.image.tag=v1.2.3 \
|
||||||
|
--set jan-inference-model.image.tag=v1.2.3
|
||||||
|
```
|
||||||
39
docs/src/pages/api-reference/index.mdx
Normal file
39
docs/src/pages/api-reference/index.mdx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
title: Jan Server
|
||||||
|
description: Self-hosted AI infrastructure running the Jan platform on Kubernetes.
|
||||||
|
keywords:
|
||||||
|
[
|
||||||
|
Jan Server,
|
||||||
|
self-hosted AI,
|
||||||
|
Kubernetes deployment,
|
||||||
|
Docker containers,
|
||||||
|
AI inference,
|
||||||
|
local LLM server,
|
||||||
|
VLLM,
|
||||||
|
Go API gateway,
|
||||||
|
Jan-v1 model
|
||||||
|
]
|
||||||
|
---
|
||||||
|
|
||||||
|
## Self-Hosted Jan Platform
|
||||||
|
|
||||||
|
Jan Server deploys the Jan AI platform on your own infrastructure using Kubernetes. It provides a complete AI inference stack with API gateway, model serving, and data persistence.
|
||||||
|
|
||||||
|
Jan Server is in early development. APIs and deployment methods may change.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
Jan Server consists of two main components:
|
||||||
|
|
||||||
|
- **API Gateway**: Go application handling authentication, web requests, and external integrations
|
||||||
|
- **Inference Model**: VLLM server running the Jan-v1-4B model for AI inference
|
||||||
|
- **PostgreSQL**: Database for user data, conversations, and system state
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
- **Kubernetes Native**: Deploys via Helm charts with minikube support
|
||||||
|
- **Jan-v1 Model**: 4B parameter model optimized for reasoning and tool use
|
||||||
|
- **OpenAI Compatible API**: Standard endpoints for integration
|
||||||
|
- **Authentication**: JWT tokens and OAuth2 Google integration
|
||||||
|
- **External Integrations**: Serper API for web search capabilities
|
||||||
|
- **Development Ready**: Local development environment with hot reload
|
||||||
151
docs/src/pages/api-reference/installation.mdx
Normal file
151
docs/src/pages/api-reference/installation.mdx
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
---
|
||||||
|
title: Installation
|
||||||
|
description: Install and deploy Jan Server on Kubernetes using minikube and Helm.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Jan Server requires the following tools installed on your system:
|
||||||
|
|
||||||
|
- **Docker**: For building container images
|
||||||
|
- **minikube**: Local Kubernetes cluster for development
|
||||||
|
- **Helm**: Package manager for Kubernetes applications
|
||||||
|
- **kubectl**: Kubernetes command-line tool (installed with minikube)
|
||||||
|
|
||||||
|
Jan Server currently supports minikube for local development. Production Kubernetes deployments are planned for future releases.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
|
||||||
|
1. **Clone the repository**
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/menloresearch/jan-server
|
||||||
|
cd jan-server
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Start minikube**
|
||||||
|
```bash
|
||||||
|
minikube start
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Configure Docker environment**
|
||||||
|
```bash
|
||||||
|
eval $(minikube docker-env)
|
||||||
|
alias kubectl="minikube kubectl --"
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Deploy Jan Server**
|
||||||
|
```bash
|
||||||
|
./scripts/run.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Access the API**
|
||||||
|
|
||||||
|
The script automatically forwards port 8080. Access the Swagger UI at:
|
||||||
|
```
|
||||||
|
http://localhost:8080/api/swagger/index.html#/
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Manual Installation
|
||||||
|
|
||||||
|
### Build Docker Images
|
||||||
|
|
||||||
|
Build both required Docker images:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build API Gateway
|
||||||
|
docker build -t jan-api-gateway:latest ./apps/jan-api-gateway
|
||||||
|
|
||||||
|
# Build Inference Model
|
||||||
|
docker build -t jan-inference-model:latest ./apps/jan-inference-model
|
||||||
|
```
|
||||||
|
|
||||||
|
The inference model image downloads the Jan-v1-4B model from Hugging Face during build. This requires an internet connection and several GB of download.
|
||||||
|
|
||||||
|
### Deploy with Helm
|
||||||
|
|
||||||
|
Install the Helm chart:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update Helm dependencies
|
||||||
|
helm dependency update ./charts/umbrella-chart
|
||||||
|
|
||||||
|
# Install Jan Server
|
||||||
|
helm install jan-server ./charts/umbrella-chart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Port Forwarding
|
||||||
|
|
||||||
|
Forward the API gateway port to access from your local machine:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl port-forward svc/jan-server-jan-api-gateway 8080:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verify Installation
|
||||||
|
|
||||||
|
Check that all pods are running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get pods
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
```
|
||||||
|
NAME READY STATUS RESTARTS
|
||||||
|
jan-server-jan-api-gateway-xxx 1/1 Running 0
|
||||||
|
jan-server-jan-inference-model-xxx 1/1 Running 0
|
||||||
|
jan-server-postgresql-0 1/1 Running 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Test the API gateway:
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/health
|
||||||
|
```
|
||||||
|
|
||||||
|
## Uninstalling
|
||||||
|
|
||||||
|
To remove Jan Server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm uninstall jan-server
|
||||||
|
```
|
||||||
|
|
||||||
|
To stop minikube:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
minikube stop
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
**Pods in `ImagePullBackOff` state**
|
||||||
|
- Ensure Docker images were built in the minikube environment
|
||||||
|
- Run `eval $(minikube docker-env)` before building images
|
||||||
|
|
||||||
|
**Port forwarding connection refused**
|
||||||
|
- Verify the service is running: `kubectl get svc`
|
||||||
|
- Check pod status: `kubectl get pods`
|
||||||
|
- Review logs: `kubectl logs deployment/jan-server-jan-api-gateway`
|
||||||
|
|
||||||
|
**Inference model download fails**
|
||||||
|
- Ensure internet connectivity during Docker build
|
||||||
|
- The Jan-v1-4B model is approximately 2.4GB
|
||||||
|
|
||||||
|
### Resource Requirements
|
||||||
|
|
||||||
|
**Minimum System Requirements:**
|
||||||
|
- 8GB RAM
|
||||||
|
- 20GB free disk space
|
||||||
|
- 4 CPU cores
|
||||||
|
|
||||||
|
**Recommended System Requirements:**
|
||||||
|
- 16GB RAM
|
||||||
|
- 50GB free disk space
|
||||||
|
- 8 CPU cores
|
||||||
|
- GPU support (for faster inference)
|
||||||
|
|
||||||
|
The inference model requires significant memory. Ensure your minikube cluster has adequate resources allocated.
|
||||||
@ -1,55 +1,51 @@
|
|||||||
{
|
{
|
||||||
"-- Switcher": {
|
"-- Switcher": {
|
||||||
"type": "separator",
|
"type": "separator",
|
||||||
"title": "Switcher"
|
"title": "Switcher"
|
||||||
},
|
},
|
||||||
"index": "Overview",
|
"get-started-separator": {
|
||||||
"getting-started-separator": {
|
"title": "Get started",
|
||||||
"title": "GETTING STARTED",
|
"type": "separator"
|
||||||
"type": "separator"
|
},
|
||||||
},
|
"index": "Overview",
|
||||||
"quickstart": "QuickStart",
|
"quickstart": "Quickstart",
|
||||||
"desktop": "Install 👋 Jan",
|
"desktop": "Install 👋 Jan",
|
||||||
"jan-models": "Models",
|
"jan-models": "Models",
|
||||||
"assistants": "Create Assistants",
|
"remote-models": "Cloud Providers",
|
||||||
"remote-models": "Cloud Providers",
|
"mcp-examples": "Tutorials",
|
||||||
"mcp-examples": "Tutorials",
|
"coreconcepts-separator": {
|
||||||
|
"title": "Core concepts",
|
||||||
"explanation-separator": {
|
"type": "separator"
|
||||||
"title": "EXPLANATION",
|
},
|
||||||
"type": "separator"
|
"assistants": "Assistants",
|
||||||
},
|
"llama-cpp": "Local AI Engine",
|
||||||
"llama-cpp": "Local AI Engine",
|
"model-parameters": "Model Parameters",
|
||||||
"model-parameters": "Model Parameters",
|
"privacy-policy": {
|
||||||
|
"type": "page",
|
||||||
"privacy-policy": {
|
"display": "hidden",
|
||||||
"type": "page",
|
"title": "Privacy Policy"
|
||||||
"display": "hidden",
|
},
|
||||||
"title": "Privacy Policy"
|
"advanced-separator": {
|
||||||
},
|
"title": "ADVANCED",
|
||||||
|
"type": "separator"
|
||||||
"advanced-separator": {
|
},
|
||||||
"title": "ADVANCED",
|
"manage-models": "Manage Models",
|
||||||
"type": "separator"
|
"mcp": "Model Context Protocol",
|
||||||
},
|
"localserver": {
|
||||||
"manage-models": "Manage Models",
|
"title": "LOCAL SERVER",
|
||||||
"mcp": "Model Context Protocol",
|
"type": "separator"
|
||||||
|
},
|
||||||
"localserver": {
|
"api-server": "Server Setup",
|
||||||
"title": "LOCAL SERVER",
|
"llama-cpp-server": "LlamaCpp Server",
|
||||||
"type": "separator"
|
"server-settings": "Server Settings",
|
||||||
},
|
"server-troubleshooting": "Server Troubleshooting",
|
||||||
"api-server": "Server Setup",
|
"server-examples": "Integrations",
|
||||||
"llama-cpp-server": "LlamaCpp Server",
|
"reference-separator": {
|
||||||
"server-settings": "Server Settings",
|
"title": "REFERENCE",
|
||||||
"server-troubleshooting": "Server Troubleshooting",
|
"type": "separator"
|
||||||
"server-examples": "Integrations",
|
},
|
||||||
"reference-separator": {
|
"settings": "Settings",
|
||||||
"title": "REFERENCE",
|
"data-folder": "Jan Data Folder",
|
||||||
"type": "separator"
|
"troubleshooting": "Troubleshooting",
|
||||||
},
|
"privacy": "Privacy"
|
||||||
"settings": "Settings",
|
|
||||||
"data-folder": "Jan Data Folder",
|
|
||||||
"troubleshooting": "Troubleshooting",
|
|
||||||
"privacy": "Privacy"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"-- Switcher": {
|
|
||||||
"type": "separator",
|
|
||||||
"title": "Switcher"
|
|
||||||
},
|
|
||||||
"index": {
|
|
||||||
"display": "hidden"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,87 +0,0 @@
|
|||||||
---
|
|
||||||
title: Coming Soon
|
|
||||||
description: Exciting new features and platforms are on the way. Stay tuned for Jan Web, Jan Mobile, and our API Platform.
|
|
||||||
keywords:
|
|
||||||
[
|
|
||||||
Jan,
|
|
||||||
Customizable Intelligence, LLM,
|
|
||||||
local AI,
|
|
||||||
privacy focus,
|
|
||||||
free and open source,
|
|
||||||
private and offline,
|
|
||||||
conversational AI,
|
|
||||||
no-subscription fee,
|
|
||||||
large language models,
|
|
||||||
coming soon,
|
|
||||||
Jan Web,
|
|
||||||
Jan Mobile,
|
|
||||||
API Platform,
|
|
||||||
]
|
|
||||||
---
|
|
||||||
|
|
||||||
import { Callout } from 'nextra/components'
|
|
||||||
|
|
||||||
<div className="text-center py-12">
|
|
||||||
<div className="mb-8">
|
|
||||||
<h1 className="text-4xl font-bold bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent mb-4 py-2">
|
|
||||||
🚀 Coming Soon
|
|
||||||
</h1>
|
|
||||||
<p className="text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">
|
|
||||||
We're working on the next stage of Jan - making our local assistant more powerful and available in more platforms.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 max-w-4xl mx-auto mb-12">
|
|
||||||
<div className="p-6 border border-gray-200 dark:border-gray-700 rounded-lg bg-gradient-to-br from-blue-50 to-indigo-50 dark:from-blue-900/20 dark:to-indigo-900/20">
|
|
||||||
<div className="text-3xl mb-3">🌐</div>
|
|
||||||
<h3 className="text-lg font-semibold mb-2">Jan Web</h3>
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
|
||||||
Access Jan directly from your browser with our powerful web interface
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="p-6 border border-gray-200 dark:border-gray-700 rounded-lg bg-gradient-to-br from-green-50 to-emerald-50 dark:from-green-900/20 dark:to-emerald-900/20">
|
|
||||||
<div className="text-3xl mb-3">📱</div>
|
|
||||||
<h3 className="text-lg font-semibold mb-2">Jan Mobile</h3>
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
|
||||||
Take Jan on the go with our native mobile applications
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="p-6 border border-gray-200 dark:border-gray-700 rounded-lg bg-gradient-to-br from-purple-50 to-pink-50 dark:from-purple-900/20 dark:to-pink-900/20">
|
|
||||||
<div className="text-3xl mb-3">⚡</div>
|
|
||||||
<h3 className="text-lg font-semibold mb-2">API Platform</h3>
|
|
||||||
<p className="text-sm text-gray-600 dark:text-gray-400">
|
|
||||||
Integrate Jan's capabilities into your applications with our API
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Callout type="info">
|
|
||||||
**Stay Updated**: Follow our [GitHub repository](https://github.com/menloresearch/jan) and join our [Discord community](https://discord.com/invite/FTk2MvZwJH) for the latest updates on these exciting releases!
|
|
||||||
</Callout>
|
|
||||||
|
|
||||||
<div className="mt-12">
|
|
||||||
<h2 className="text-2xl font-semibold mb-6">What to Expect</h2>
|
|
||||||
<div className="text-left max-w-2xl mx-auto space-y-4">
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<span className="text-green-500 text-xl">✓</span>
|
|
||||||
<div>
|
|
||||||
<strong>Seamless Experience:</strong> Unified interface across all platforms
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<span className="text-green-500 text-xl">✓</span>
|
|
||||||
<div>
|
|
||||||
<strong>Privacy First:</strong> Same privacy-focused approach you trust
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-start gap-3">
|
|
||||||
<span className="text-green-500 text-xl">✓</span>
|
|
||||||
<div>
|
|
||||||
<strong>Developer Friendly:</strong> Robust APIs and comprehensive documentation
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
24
docs/src/pages/server/_meta.json
Normal file
24
docs/src/pages/server/_meta.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"-- Switcher": {
|
||||||
|
"type": "separator",
|
||||||
|
"title": "Switcher"
|
||||||
|
},
|
||||||
|
"get-started-separator": {
|
||||||
|
"title": "Get started",
|
||||||
|
"type": "separator"
|
||||||
|
},
|
||||||
|
"index": "Overview",
|
||||||
|
"installation": "Installation",
|
||||||
|
"configuration": "Configuration",
|
||||||
|
"core-concepts-separator": {
|
||||||
|
"title": "Core concepts",
|
||||||
|
"type": "separator"
|
||||||
|
},
|
||||||
|
"api-reference": "API Reference",
|
||||||
|
"resource-separator": {
|
||||||
|
"title": "Resources",
|
||||||
|
"type": "separator"
|
||||||
|
},
|
||||||
|
"architecture": "Architecture",
|
||||||
|
"development": "Development"
|
||||||
|
}
|
||||||
378
docs/src/pages/server/api-reference.mdx
Normal file
378
docs/src/pages/server/api-reference.mdx
Normal file
@ -0,0 +1,378 @@
|
|||||||
|
---
|
||||||
|
title: API Reference
|
||||||
|
description: Complete API documentation for Jan Server endpoints and OpenAI compatibility.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Base URL
|
||||||
|
|
||||||
|
All API endpoints are available at the API gateway base URL:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:8080/api/v1
|
||||||
|
```
|
||||||
|
|
||||||
|
The API gateway automatically forwards port 8080 when using the standard deployment scripts.
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
Jan Server supports multiple authentication methods:
|
||||||
|
|
||||||
|
### JWT Token Authentication
|
||||||
|
|
||||||
|
Include JWT token in the Authorization header:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer <jwt_token>" \
|
||||||
|
http://localhost:8080/api/v1/protected-endpoint
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Key Authentication
|
||||||
|
|
||||||
|
Include API key in the Authorization header:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer <api_key>" \
|
||||||
|
http://localhost:8080/api/v1/protected-endpoint
|
||||||
|
```
|
||||||
|
|
||||||
|
## OpenAI-Compatible Endpoints
|
||||||
|
|
||||||
|
Jan Server implements OpenAI-compatible endpoints for seamless integration with existing tools.
|
||||||
|
|
||||||
|
### Chat Completions
|
||||||
|
|
||||||
|
**Endpoint**: `POST /api/v1/chat/completions`
|
||||||
|
|
||||||
|
Standard OpenAI chat completions API for conversational AI.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/api/v1/chat/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer <token>" \
|
||||||
|
-d '{
|
||||||
|
"model": "jan-v1-4b",
|
||||||
|
"messages": [
|
||||||
|
{"role": "user", "content": "Hello, how are you?"}
|
||||||
|
],
|
||||||
|
"max_tokens": 100,
|
||||||
|
"temperature": 0.7
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters:**
|
||||||
|
- `model` (string): Model identifier (`jan-v1-4b`)
|
||||||
|
- `messages` (array): Conversation history
|
||||||
|
- `max_tokens` (integer): Maximum response tokens
|
||||||
|
- `temperature` (float): Response randomness (0.0 to 2.0)
|
||||||
|
- `stream` (boolean): Enable streaming responses
|
||||||
|
|
||||||
|
### Model Information
|
||||||
|
|
||||||
|
**Endpoint**: `GET /api/v1/models`
|
||||||
|
|
||||||
|
List available models:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/api/v1/models
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"object": "list",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"id": "jan-v1-4b",
|
||||||
|
"object": "model",
|
||||||
|
"created": 1234567890,
|
||||||
|
"owned_by": "jan"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Completions (Text Generation)
|
||||||
|
|
||||||
|
**Endpoint**: `POST /api/v1/completions`
|
||||||
|
|
||||||
|
Text completion endpoint:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/api/v1/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer <token>" \
|
||||||
|
-d '{
|
||||||
|
"model": "jan-v1-4b",
|
||||||
|
"prompt": "The meaning of life is",
|
||||||
|
"max_tokens": 50
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authentication Endpoints
|
||||||
|
|
||||||
|
### OAuth2 Google Login
|
||||||
|
|
||||||
|
**Endpoint**: `GET /auth/google`
|
||||||
|
|
||||||
|
Redirects to Google OAuth2 authorization:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/auth/google
|
||||||
|
```
|
||||||
|
|
||||||
|
### OAuth2 Callback
|
||||||
|
|
||||||
|
**Endpoint**: `GET /auth/google/callback`
|
||||||
|
|
||||||
|
Handles OAuth2 callback and issues JWT token:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:8080/auth/google/callback?code=<auth_code>&state=<state>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Token Refresh
|
||||||
|
|
||||||
|
**Endpoint**: `POST /api/v1/auth/refresh`
|
||||||
|
|
||||||
|
Refresh expired JWT tokens:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/api/v1/auth/refresh \
|
||||||
|
-H "Authorization: Bearer <expired_token>"
|
||||||
|
```
|
||||||
|
|
||||||
|
## User Management
|
||||||
|
|
||||||
|
### User Profile
|
||||||
|
|
||||||
|
**Endpoint**: `GET /api/v1/user/profile`
|
||||||
|
|
||||||
|
Get current user profile:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer <token>" \
|
||||||
|
http://localhost:8080/api/v1/user/profile
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Keys
|
||||||
|
|
||||||
|
**Endpoint**: `POST /api/v1/user/api-keys`
|
||||||
|
|
||||||
|
Generate new API key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/api/v1/user/api-keys \
|
||||||
|
-H "Authorization: Bearer <token>" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"name": "Development Key",
|
||||||
|
"permissions": ["read", "write"]
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conversation Management
|
||||||
|
|
||||||
|
### Create Conversation
|
||||||
|
|
||||||
|
**Endpoint**: `POST /api/v1/conversations`
|
||||||
|
|
||||||
|
Create new conversation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:8080/api/v1/conversations \
|
||||||
|
-H "Authorization: Bearer <token>" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"title": "My Conversation",
|
||||||
|
"model": "jan-v1-4b"
|
||||||
|
}'
|
||||||
|
```
|
||||||
|
|
||||||
|
### List Conversations
|
||||||
|
|
||||||
|
**Endpoint**: `GET /api/v1/conversations`
|
||||||
|
|
||||||
|
Get user's conversations:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer <token>" \
|
||||||
|
http://localhost:8080/api/v1/conversations
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get Conversation
|
||||||
|
|
||||||
|
**Endpoint**: `GET /api/v1/conversations/{id}`
|
||||||
|
|
||||||
|
Get specific conversation with message history:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer <token>" \
|
||||||
|
http://localhost:8080/api/v1/conversations/123
|
||||||
|
```
|
||||||
|
|
||||||
|
## Health and Status
|
||||||
|
|
||||||
|
### Health Check
|
||||||
|
|
||||||
|
**Endpoint**: `GET /health`
|
||||||
|
|
||||||
|
Basic health check:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/health
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "ok",
|
||||||
|
"timestamp": "2024-01-01T12:00:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### System Status
|
||||||
|
|
||||||
|
**Endpoint**: `GET /api/v1/status`
|
||||||
|
|
||||||
|
Detailed system status:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -H "Authorization: Bearer <token>" \
|
||||||
|
http://localhost:8080/api/v1/status
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response:**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"api_gateway": "healthy",
|
||||||
|
"inference_model": "healthy",
|
||||||
|
"database": "healthy",
|
||||||
|
"external_apis": {
|
||||||
|
"serper": "healthy"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Responses
|
||||||
|
|
||||||
|
Jan Server returns standard HTTP status codes and JSON error responses:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": {
|
||||||
|
"message": "Invalid request format",
|
||||||
|
"type": "invalid_request_error",
|
||||||
|
"code": "invalid_json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Error Codes
|
||||||
|
|
||||||
|
| Status Code | Description |
|
||||||
|
|-------------|-------------|
|
||||||
|
| `400` | Bad Request - Invalid request format |
|
||||||
|
| `401` | Unauthorized - Invalid or missing authentication |
|
||||||
|
| `403` | Forbidden - Insufficient permissions |
|
||||||
|
| `404` | Not Found - Resource not found |
|
||||||
|
| `429` | Too Many Requests - Rate limit exceeded |
|
||||||
|
| `500` | Internal Server Error - Server error |
|
||||||
|
| `503` | Service Unavailable - Service temporarily unavailable |
|
||||||
|
|
||||||
|
## Interactive Documentation
|
||||||
|
|
||||||
|
Jan Server provides interactive Swagger documentation at:
|
||||||
|
|
||||||
|
```
|
||||||
|
http://localhost:8080/api/swagger/index.html#/
|
||||||
|
```
|
||||||
|
|
||||||
|
This interface allows you to:
|
||||||
|
- Browse all available endpoints
|
||||||
|
- Test API calls directly from the browser
|
||||||
|
- View request/response schemas
|
||||||
|
- Generate code samples
|
||||||
|
|
||||||
|
The Swagger documentation is auto-generated from Go code annotations and provides the most up-to-date API reference.
|
||||||
|
|
||||||
|
## Rate Limiting
|
||||||
|
|
||||||
|
API endpoints implement rate limiting to prevent abuse:
|
||||||
|
|
||||||
|
- **Authenticated requests**: 1000 requests per hour per user
|
||||||
|
- **Unauthenticated requests**: 100 requests per hour per IP
|
||||||
|
- **Model inference**: 60 requests per minute per user
|
||||||
|
|
||||||
|
Rate limit headers are included in responses:
|
||||||
|
```
|
||||||
|
X-RateLimit-Limit: 1000
|
||||||
|
X-RateLimit-Remaining: 999
|
||||||
|
X-RateLimit-Reset: 1609459200
|
||||||
|
```
|
||||||
|
|
||||||
|
## SDK and Client Libraries
|
||||||
|
|
||||||
|
### JavaScript/Node.js
|
||||||
|
|
||||||
|
Use the OpenAI JavaScript SDK with Jan Server:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
import OpenAI from 'openai';
|
||||||
|
|
||||||
|
const openai = new OpenAI({
|
||||||
|
baseURL: 'http://localhost:8080/api/v1',
|
||||||
|
apiKey: 'your-jwt-token'
|
||||||
|
});
|
||||||
|
|
||||||
|
const completion = await openai.chat.completions.create({
|
||||||
|
model: 'jan-v1-4b',
|
||||||
|
messages: [
|
||||||
|
{ role: 'user', content: 'Hello!' }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Python
|
||||||
|
|
||||||
|
Use the OpenAI Python SDK:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import openai
|
||||||
|
|
||||||
|
openai.api_base = "http://localhost:8080/api/v1"
|
||||||
|
openai.api_key = "your-jwt-token"
|
||||||
|
|
||||||
|
response = openai.ChatCompletion.create(
|
||||||
|
model="jan-v1-4b",
|
||||||
|
messages=[
|
||||||
|
{"role": "user", "content": "Hello!"}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### cURL Examples
|
||||||
|
|
||||||
|
Complete cURL examples for common operations:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Get models
|
||||||
|
curl http://localhost:8080/api/v1/models
|
||||||
|
|
||||||
|
# Chat completion
|
||||||
|
curl -X POST http://localhost:8080/api/v1/chat/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"model": "jan-v1-4b",
|
||||||
|
"messages": [{"role": "user", "content": "Hello"}]
|
||||||
|
}'
|
||||||
|
|
||||||
|
# Streaming chat completion
|
||||||
|
curl -X POST http://localhost:8080/api/v1/chat/completions \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-d '{
|
||||||
|
"model": "jan-v1-4b",
|
||||||
|
"messages": [{"role": "user", "content": "Tell me a story"}],
|
||||||
|
"stream": true
|
||||||
|
}' \
|
||||||
|
--no-buffer
|
||||||
|
```
|
||||||
191
docs/src/pages/server/architecture.mdx
Normal file
191
docs/src/pages/server/architecture.mdx
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
---
|
||||||
|
title: Architecture
|
||||||
|
description: Technical architecture and system design of Jan Server components.
|
||||||
|
---
|
||||||
|
|
||||||
|
## System Overview
|
||||||
|
|
||||||
|
Jan Server implements a microservices architecture on Kubernetes with three core components communicating over HTTP and managed by Helm charts.
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
Client[Client/Browser] --> Gateway[jan-api-gateway:8080]
|
||||||
|
Gateway --> Model[jan-inference-model:8101]
|
||||||
|
Gateway --> DB[(PostgreSQL:5432)]
|
||||||
|
Gateway --> Serper[Serper API]
|
||||||
|
Gateway --> OAuth[Google OAuth2]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Components
|
||||||
|
|
||||||
|
### API Gateway (`jan-api-gateway`)
|
||||||
|
|
||||||
|
**Technology Stack:**
|
||||||
|
- **Language**: Go 1.24.6
|
||||||
|
- **Framework**: Gin web framework
|
||||||
|
- **ORM**: GORM with PostgreSQL driver
|
||||||
|
- **DI**: Google Wire for dependency injection
|
||||||
|
- **Documentation**: Swagger/OpenAPI auto-generated
|
||||||
|
|
||||||
|
**Responsibilities:**
|
||||||
|
- HTTP request routing and middleware
|
||||||
|
- User authentication via JWT and OAuth2
|
||||||
|
- Database operations and data persistence
|
||||||
|
- External API integration (Serper, Google OAuth)
|
||||||
|
- OpenAI-compatible API endpoints
|
||||||
|
- Request forwarding to inference service
|
||||||
|
|
||||||
|
**Key Directories:**
|
||||||
|
```
|
||||||
|
application/
|
||||||
|
├── cmd/server/ # Main entry point and DI wiring
|
||||||
|
├── app/ # Core business logic
|
||||||
|
├── config/ # Environment variables and settings
|
||||||
|
└── docs/ # Auto-generated Swagger docs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Inference Model (`jan-inference-model`)
|
||||||
|
|
||||||
|
**Technology Stack:**
|
||||||
|
- **Base Image**: VLLM OpenAI v0.10.0
|
||||||
|
- **Model**: Jan-v1-4B (downloaded from Hugging Face)
|
||||||
|
- **Protocol**: OpenAI-compatible HTTP API
|
||||||
|
- **Features**: Tool calling, reasoning parsing
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- **Model Path**: `/models/Jan-v1-4B`
|
||||||
|
- **Served Name**: `jan-v1-4b`
|
||||||
|
- **Port**: 8101
|
||||||
|
- **Batch Tokens**: 1024 max
|
||||||
|
- **Tool Parser**: Hermes
|
||||||
|
- **Reasoning Parser**: Qwen3
|
||||||
|
|
||||||
|
**Capabilities:**
|
||||||
|
- Text generation and completion
|
||||||
|
- Tool calling and function execution
|
||||||
|
- Multi-turn conversations
|
||||||
|
- Reasoning and chain-of-thought
|
||||||
|
|
||||||
|
### Database (PostgreSQL)
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- **Database**: `jan`
|
||||||
|
- **User**: `jan-user`
|
||||||
|
- **Password**: `jan-password`
|
||||||
|
- **Port**: 5432
|
||||||
|
|
||||||
|
**Schema:**
|
||||||
|
- User accounts and authentication
|
||||||
|
- Conversation history
|
||||||
|
- Project and organization management
|
||||||
|
- API keys and access control
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
### Request Processing
|
||||||
|
|
||||||
|
1. **Client Request**: HTTP request to API gateway on port 8080
|
||||||
|
2. **Authentication**: JWT token validation or OAuth2 flow
|
||||||
|
3. **Request Routing**: Gateway routes to appropriate handler
|
||||||
|
4. **Database Operations**: GORM queries for user data/state
|
||||||
|
5. **Inference Call**: HTTP request to model service on port 8101
|
||||||
|
6. **Response Assembly**: Gateway combines results and returns to client
|
||||||
|
|
||||||
|
### Authentication Flow
|
||||||
|
|
||||||
|
**JWT Authentication:**
|
||||||
|
1. User provides credentials
|
||||||
|
2. Gateway validates against database
|
||||||
|
3. JWT token issued with HMAC-SHA256 signing
|
||||||
|
4. Subsequent requests include JWT in Authorization header
|
||||||
|
|
||||||
|
**OAuth2 Flow:**
|
||||||
|
1. Client redirected to Google OAuth2
|
||||||
|
2. Authorization code returned to redirect URL
|
||||||
|
3. Gateway exchanges code for access token
|
||||||
|
4. User profile retrieved from Google
|
||||||
|
5. Local JWT token issued
|
||||||
|
|
||||||
|
## Deployment Architecture
|
||||||
|
|
||||||
|
### Kubernetes Resources
|
||||||
|
|
||||||
|
**Deployments:**
|
||||||
|
- `jan-api-gateway`: Single replica Go application
|
||||||
|
- `jan-inference-model`: Single replica VLLM server
|
||||||
|
- `postgresql`: StatefulSet with persistent storage
|
||||||
|
|
||||||
|
**Services:**
|
||||||
|
- `jan-api-gateway`: ClusterIP exposing port 8080
|
||||||
|
- `jan-inference-model`: ClusterIP exposing port 8101
|
||||||
|
- `postgresql`: ClusterIP exposing port 5432
|
||||||
|
|
||||||
|
**Configuration:**
|
||||||
|
- Environment variables via Helm values
|
||||||
|
- Secrets for sensitive data (JWT keys, OAuth credentials)
|
||||||
|
- ConfigMaps for application settings
|
||||||
|
|
||||||
|
### Helm Chart Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
charts/
|
||||||
|
├── umbrella-chart/ # Main deployment chart
|
||||||
|
│ ├── Chart.yaml
|
||||||
|
│ ├── values.yaml # Configuration values
|
||||||
|
│ └── Chart.lock
|
||||||
|
└── apps-charts/ # Individual service charts
|
||||||
|
├── jan-api-gateway/
|
||||||
|
└── jan-inference-model/
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Architecture
|
||||||
|
|
||||||
|
### Authentication Methods
|
||||||
|
- **JWT Tokens**: HMAC-SHA256 signed tokens for API access
|
||||||
|
- **OAuth2**: Google OAuth2 integration for user login
|
||||||
|
- **API Keys**: HMAC-SHA256 signed keys for service access
|
||||||
|
|
||||||
|
### Network Security
|
||||||
|
- **Internal Communication**: Services communicate over Kubernetes cluster network
|
||||||
|
- **External Access**: Only API gateway exposed via port forwarding or ingress
|
||||||
|
- **Database Access**: PostgreSQL accessible only within cluster
|
||||||
|
|
||||||
|
### Data Security
|
||||||
|
- **Secrets Management**: Kubernetes secrets for sensitive configuration
|
||||||
|
- **Environment Variables**: Non-sensitive config via environment variables
|
||||||
|
- **Database Encryption**: Standard PostgreSQL encryption at rest
|
||||||
|
|
||||||
|
Production deployments should implement additional security measures including TLS termination, network policies, and secret rotation.
|
||||||
|
|
||||||
|
## Scalability Considerations
|
||||||
|
|
||||||
|
**Current Limitations:**
|
||||||
|
- Single replica deployments
|
||||||
|
- No horizontal pod autoscaling
|
||||||
|
- Local storage for database
|
||||||
|
|
||||||
|
**Future Enhancements:**
|
||||||
|
- Multi-replica API gateway with load balancing
|
||||||
|
- Horizontal pod autoscaling based on CPU/memory
|
||||||
|
- External database with clustering
|
||||||
|
- Redis caching layer
|
||||||
|
- Message queue for async processing
|
||||||
|
|
||||||
|
## Development Architecture
|
||||||
|
|
||||||
|
### Code Generation
|
||||||
|
- **Swagger**: API documentation generated from Go annotations
|
||||||
|
- **Wire**: Dependency injection code generated from providers
|
||||||
|
- **GORM Gen**: Database model generation from schema
|
||||||
|
|
||||||
|
### Build Process
|
||||||
|
1. **API Gateway**: Multi-stage Docker build with Go compilation
|
||||||
|
2. **Inference Model**: Base VLLM image with model download
|
||||||
|
3. **Helm Charts**: Dependency management and templating
|
||||||
|
4. **Documentation**: Auto-generation during development
|
||||||
|
|
||||||
|
### Local Development
|
||||||
|
- **Hot Reload**: Source code changes reflected without full rebuild
|
||||||
|
- **Database Migrations**: Automated schema updates
|
||||||
|
- **API Testing**: Swagger UI for interactive testing
|
||||||
|
- **Logging**: Structured logging with configurable levels
|
||||||
263
docs/src/pages/server/configuration.mdx
Normal file
263
docs/src/pages/server/configuration.mdx
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
---
|
||||||
|
title: Configuration
|
||||||
|
description: Configure Jan Server environment variables, authentication, and external integrations.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
Jan Server configuration is managed through environment variables defined in the Helm values file at `charts/umbrella-chart/values.yaml`.
|
||||||
|
|
||||||
|
### API Gateway Configuration
|
||||||
|
|
||||||
|
#### Core Settings
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `JAN_INFERENCE_MODEL_URL` | `http://jan-server-jan-inference-model:8101` | Internal URL for inference service |
|
||||||
|
|
||||||
|
#### Authentication
|
||||||
|
|
||||||
|
| Variable | Purpose | Format |
|
||||||
|
|----------|---------|--------|
|
||||||
|
| `JWT_SECRET` | JWT token signing | Base64 encoded HMAC-SHA256 key |
|
||||||
|
| `APIKEY_SECRET` | API key signing | Base64 encoded HMAC-SHA256 key |
|
||||||
|
|
||||||
|
The default JWT and API key secrets are for development only. Generate new secrets for production deployments.
|
||||||
|
|
||||||
|
#### OAuth2 Integration
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `OAUTH2_GOOGLE_CLIENT_ID` | Google OAuth2 application client ID |
|
||||||
|
| `OAUTH2_GOOGLE_CLIENT_SECRET` | Google OAuth2 application secret |
|
||||||
|
| `OAUTH2_GOOGLE_REDIRECT_URL` | Callback URL for OAuth2 flow |
|
||||||
|
|
||||||
|
#### External APIs
|
||||||
|
|
||||||
|
| Variable | Provider | Purpose |
|
||||||
|
|----------|----------|---------|
|
||||||
|
| `SERPER_API_KEY` | Serper | Web search integration |
|
||||||
|
|
||||||
|
#### Database Connection
|
||||||
|
|
||||||
|
| Variable | Default | Description |
|
||||||
|
|----------|---------|-------------|
|
||||||
|
| `DB_POSTGRESQL_WRITE_DSN` | `host=jan-server-postgresql user=jan-user password=jan-password dbname=jan port=5432 sslmode=disable` | Write database connection |
|
||||||
|
| `DB_POSTGRESQL_READ1_DSN` | `host=jan-server-postgresql user=jan-user password=jan-password dbname=jan port=5432 sslmode=disable` | Read database connection |
|
||||||
|
|
||||||
|
## Helm Configuration
|
||||||
|
|
||||||
|
### Updating Values
|
||||||
|
|
||||||
|
Edit the configuration in `charts/umbrella-chart/values.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jan-api-gateway:
|
||||||
|
env:
|
||||||
|
- name: SERPER_API_KEY
|
||||||
|
value: your_serper_api_key
|
||||||
|
- name: OAUTH2_GOOGLE_CLIENT_ID
|
||||||
|
value: your_google_client_id
|
||||||
|
- name: OAUTH2_GOOGLE_CLIENT_SECRET
|
||||||
|
value: your_google_client_secret
|
||||||
|
```
|
||||||
|
|
||||||
|
### Applying Changes
|
||||||
|
|
||||||
|
After modifying values, redeploy the application:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm upgrade jan-server ./charts/umbrella-chart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authentication Setup
|
||||||
|
|
||||||
|
### JWT Tokens
|
||||||
|
|
||||||
|
Generate a secure JWT signing key:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate 256-bit key for HMAC-SHA256
|
||||||
|
openssl rand -base64 32
|
||||||
|
```
|
||||||
|
|
||||||
|
Update the `JWT_SECRET` value in your Helm configuration.
|
||||||
|
|
||||||
|
### API Keys
|
||||||
|
|
||||||
|
Generate a secure API key signing secret:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Generate 256-bit key for HMAC-SHA256
|
||||||
|
openssl rand -base64 32
|
||||||
|
```
|
||||||
|
|
||||||
|
Update the `APIKEY_SECRET` value in your Helm configuration.
|
||||||
|
|
||||||
|
### Google OAuth2
|
||||||
|
|
||||||
|
1. **Create Google Cloud Project**
|
||||||
|
- Go to [Google Cloud Console](https://console.cloud.google.com)
|
||||||
|
- Create a new project or select existing
|
||||||
|
|
||||||
|
2. **Enable OAuth2**
|
||||||
|
- Navigate to "APIs & Services" > "Credentials"
|
||||||
|
- Create OAuth2 client ID credentials
|
||||||
|
- Set application type to "Web application"
|
||||||
|
|
||||||
|
3. **Configure Redirect URI**
|
||||||
|
```
|
||||||
|
http://localhost:8080/auth/google/callback
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Update Configuration**
|
||||||
|
- Set `OAUTH2_GOOGLE_CLIENT_ID` to your client ID
|
||||||
|
- Set `OAUTH2_GOOGLE_CLIENT_SECRET` to your client secret
|
||||||
|
- Set `OAUTH2_GOOGLE_REDIRECT_URL` to your callback URL
|
||||||
|
|
||||||
|
## External Integrations
|
||||||
|
|
||||||
|
### Serper API
|
||||||
|
|
||||||
|
Jan Server integrates with Serper for web search capabilities.
|
||||||
|
|
||||||
|
1. **Get API Key**
|
||||||
|
- Register at [serper.dev](https://serper.dev)
|
||||||
|
- Generate API key from dashboard
|
||||||
|
|
||||||
|
2. **Configure**
|
||||||
|
- Set `SERPER_API_KEY` in Helm values
|
||||||
|
- Redeploy the application
|
||||||
|
|
||||||
|
### Adding New Integrations
|
||||||
|
|
||||||
|
To add new external API integrations:
|
||||||
|
|
||||||
|
1. **Update Helm Values**
|
||||||
|
```yaml
|
||||||
|
jan-api-gateway:
|
||||||
|
env:
|
||||||
|
- name: YOUR_API_KEY
|
||||||
|
value: your_api_key_value
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Update Go Configuration**
|
||||||
|
|
||||||
|
Add to `config/environment_variables/env.go`:
|
||||||
|
```go
|
||||||
|
YourAPIKey string `env:"YOUR_API_KEY"`
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Redeploy**
|
||||||
|
```bash
|
||||||
|
helm upgrade jan-server ./charts/umbrella-chart
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database Configuration
|
||||||
|
|
||||||
|
### Connection Settings
|
||||||
|
|
||||||
|
The default PostgreSQL configuration uses:
|
||||||
|
- **Host**: `jan-server-postgresql` (Kubernetes service name)
|
||||||
|
- **Database**: `jan`
|
||||||
|
- **User**: `jan-user`
|
||||||
|
- **Password**: `jan-password`
|
||||||
|
- **Port**: `5432`
|
||||||
|
- **SSL**: Disabled (development only)
|
||||||
|
|
||||||
|
### Production Database
|
||||||
|
|
||||||
|
For production deployments:
|
||||||
|
|
||||||
|
1. **External Database**
|
||||||
|
- Use managed PostgreSQL service (AWS RDS, Google Cloud SQL)
|
||||||
|
- Update DSN variables with external connection details
|
||||||
|
|
||||||
|
2. **SSL/TLS**
|
||||||
|
- Enable `sslmode=require` in connection strings
|
||||||
|
- Configure certificate validation
|
||||||
|
|
||||||
|
3. **Connection Pooling**
|
||||||
|
- Consider using connection pooler (PgBouncer, pgpool-II)
|
||||||
|
- Configure appropriate pool sizes
|
||||||
|
|
||||||
|
## Model Configuration
|
||||||
|
|
||||||
|
The inference model service is configured via Docker CMD parameters:
|
||||||
|
|
||||||
|
```dockerfile
|
||||||
|
CMD ["--model", "/models/Jan-v1-4B", \
|
||||||
|
"--served-model-name", "jan-v1-4b", \
|
||||||
|
"--host", "0.0.0.0", \
|
||||||
|
"--port", "8101", \
|
||||||
|
"--max-num-batched-tokens", "1024", \
|
||||||
|
"--enable-auto-tool-choice", \
|
||||||
|
"--tool-call-parser", "hermes", \
|
||||||
|
"--reasoning-parser", "qwen3"]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Model Parameters
|
||||||
|
|
||||||
|
| Parameter | Value | Description |
|
||||||
|
|-----------|-------|-------------|
|
||||||
|
| `--model` | `/models/Jan-v1-4B` | Path to model files |
|
||||||
|
| `--served-model-name` | `jan-v1-4b` | API model identifier |
|
||||||
|
| `--max-num-batched-tokens` | `1024` | Maximum tokens per batch |
|
||||||
|
| `--tool-call-parser` | `hermes` | Tool calling format |
|
||||||
|
| `--reasoning-parser` | `qwen3` | Reasoning output format |
|
||||||
|
|
||||||
|
Model configuration changes require rebuilding the inference Docker image. This will be configurable via environment variables in future releases.
|
||||||
|
|
||||||
|
## Resource Configuration
|
||||||
|
|
||||||
|
### Kubernetes Resources
|
||||||
|
|
||||||
|
Current deployments use default resource limits. For production:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jan-api-gateway:
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
limits:
|
||||||
|
cpu: 500m
|
||||||
|
memory: 512Mi
|
||||||
|
|
||||||
|
jan-inference-model:
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 1000m
|
||||||
|
memory: 4Gi
|
||||||
|
limits:
|
||||||
|
cpu: 4000m
|
||||||
|
memory: 8Gi
|
||||||
|
```
|
||||||
|
|
||||||
|
### Storage
|
||||||
|
|
||||||
|
PostgreSQL uses default Kubernetes storage. For production:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
postgresql:
|
||||||
|
persistence:
|
||||||
|
enabled: true
|
||||||
|
size: 20Gi
|
||||||
|
storageClass: fast-ssd
|
||||||
|
```
|
||||||
|
|
||||||
|
## Logging Configuration
|
||||||
|
|
||||||
|
Configure logging levels via environment variables:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
jan-api-gateway:
|
||||||
|
env:
|
||||||
|
- name: LOG_LEVEL
|
||||||
|
value: info
|
||||||
|
- name: LOG_FORMAT
|
||||||
|
value: json
|
||||||
|
```
|
||||||
|
|
||||||
|
Available log levels: `debug`, `info`, `warn`, `error`
|
||||||
|
Available formats: `text`, `json`
|
||||||
445
docs/src/pages/server/development.mdx
Normal file
445
docs/src/pages/server/development.mdx
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
---
|
||||||
|
title: Development
|
||||||
|
description: Development setup, workflow, and contribution guidelines for Jan Server.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Development Setup
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
- **Go**: 1.24.6 or later
|
||||||
|
- **Docker**: For containerization
|
||||||
|
- **minikube**: Local Kubernetes development
|
||||||
|
- **Helm**: Package management
|
||||||
|
- **Make**: Build automation
|
||||||
|
|
||||||
|
### Initial Setup
|
||||||
|
|
||||||
|
|
||||||
|
1. **Clone Repository**
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/menloresearch/jan-server
|
||||||
|
cd jan-server
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Install Development Tools**
|
||||||
|
```bash
|
||||||
|
cd apps/jan-api-gateway/application
|
||||||
|
make install
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Generate Code**
|
||||||
|
```bash
|
||||||
|
make setup
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Start Development Environment**
|
||||||
|
```bash
|
||||||
|
# From project root
|
||||||
|
./scripts/run.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Gateway Development
|
||||||
|
|
||||||
|
### Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
apps/jan-api-gateway/application/
|
||||||
|
├── cmd/server/ # Entry point and dependency injection
|
||||||
|
│ ├── server.go # Main server setup
|
||||||
|
│ ├── wire.go # DI configuration
|
||||||
|
│ └── wire_gen.go # Generated DI code
|
||||||
|
├── app/ # Core application logic
|
||||||
|
│ ├── domain/ # Business entities
|
||||||
|
│ ├── repository/ # Data access layer
|
||||||
|
│ ├── service/ # Business logic
|
||||||
|
│ └── handler/ # HTTP handlers
|
||||||
|
├── config/ # Configuration management
|
||||||
|
└── docs/ # Generated API documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install development dependencies
|
||||||
|
make install
|
||||||
|
|
||||||
|
# Generate API documentation
|
||||||
|
make doc
|
||||||
|
|
||||||
|
# Generate dependency injection code
|
||||||
|
make wire
|
||||||
|
|
||||||
|
# Complete setup (doc + wire)
|
||||||
|
make setup
|
||||||
|
|
||||||
|
# Build application
|
||||||
|
go build -o jan-api-gateway ./cmd/server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Generation
|
||||||
|
|
||||||
|
Jan Server uses code generation for several components:
|
||||||
|
|
||||||
|
**Swagger Documentation:**
|
||||||
|
```bash
|
||||||
|
# Generates docs/swagger.json and docs/swagger.yaml
|
||||||
|
swag init --parseDependency -g cmd/server/server.go -o docs
|
||||||
|
```
|
||||||
|
|
||||||
|
**Dependency Injection:**
|
||||||
|
```bash
|
||||||
|
# Generates wire_gen.go from wire.go providers
|
||||||
|
wire ./cmd/server
|
||||||
|
```
|
||||||
|
|
||||||
|
**Database Models:**
|
||||||
|
```bash
|
||||||
|
# Generate GORM models (when schema changes)
|
||||||
|
go run cmd/codegen/gorm/gorm.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Local Development
|
||||||
|
|
||||||
|
#### Running API Gateway Locally
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd apps/jan-api-gateway/application
|
||||||
|
|
||||||
|
# Set environment variables
|
||||||
|
export JAN_INFERENCE_MODEL_URL=http://localhost:8101
|
||||||
|
export JWT_SECRET=your-jwt-secret
|
||||||
|
export DB_POSTGRESQL_WRITE_DSN="host=localhost user=jan-user password=jan-password dbname=jan port=5432 sslmode=disable"
|
||||||
|
|
||||||
|
# Run the server
|
||||||
|
go run ./cmd/server
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Database Setup
|
||||||
|
|
||||||
|
For local development, you can run PostgreSQL directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Using Docker
|
||||||
|
docker run -d \
|
||||||
|
--name jan-postgres \
|
||||||
|
-e POSTGRES_DB=jan \
|
||||||
|
-e POSTGRES_USER=jan-user \
|
||||||
|
-e POSTGRES_PASSWORD=jan-password \
|
||||||
|
-p 5432:5432 \
|
||||||
|
postgres:14
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Running Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all tests
|
||||||
|
go test ./...
|
||||||
|
|
||||||
|
# Run tests with coverage
|
||||||
|
go test -cover ./...
|
||||||
|
|
||||||
|
# Run specific test package
|
||||||
|
go test ./app/service/...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
app/
|
||||||
|
├── service/
|
||||||
|
│ ├── auth_service.go
|
||||||
|
│ ├── auth_service_test.go
|
||||||
|
│ ├── conversation_service.go
|
||||||
|
│ └── conversation_service_test.go
|
||||||
|
└── handler/
|
||||||
|
├── auth_handler.go
|
||||||
|
├── auth_handler_test.go
|
||||||
|
├── chat_handler.go
|
||||||
|
└── chat_handler_test.go
|
||||||
|
```
|
||||||
|
|
||||||
|
### Writing Tests
|
||||||
|
|
||||||
|
Example service test:
|
||||||
|
|
||||||
|
```go
|
||||||
|
func TestAuthService_ValidateToken(t *testing.T) {
|
||||||
|
// Setup
|
||||||
|
service := NewAuthService(mockRepo, mockConfig)
|
||||||
|
|
||||||
|
// Test cases
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
token string
|
||||||
|
expectValid bool
|
||||||
|
expectError bool
|
||||||
|
}{
|
||||||
|
{"valid token", "valid.jwt.token", true, false},
|
||||||
|
{"invalid token", "invalid.token", false, true},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
valid, err := service.ValidateToken(tt.token)
|
||||||
|
assert.Equal(t, tt.expectValid, valid)
|
||||||
|
assert.Equal(t, tt.expectError, err != nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docker Development
|
||||||
|
|
||||||
|
### Building Images
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build API gateway
|
||||||
|
docker build -t jan-api-gateway:dev ./apps/jan-api-gateway
|
||||||
|
|
||||||
|
# Build inference model
|
||||||
|
docker build -t jan-inference-model:dev ./apps/jan-inference-model
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development Compose
|
||||||
|
|
||||||
|
For local development without Kubernetes:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.dev.yml
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:14
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: jan
|
||||||
|
POSTGRES_USER: jan-user
|
||||||
|
POSTGRES_PASSWORD: jan-password
|
||||||
|
ports:
|
||||||
|
- "5432:5432"
|
||||||
|
|
||||||
|
api-gateway:
|
||||||
|
build: ./apps/jan-api-gateway
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
environment:
|
||||||
|
- JAN_INFERENCE_MODEL_URL=http://inference-model:8101
|
||||||
|
- DB_POSTGRESQL_WRITE_DSN=host=postgres user=jan-user password=jan-password dbname=jan port=5432 sslmode=disable
|
||||||
|
depends_on:
|
||||||
|
- postgres
|
||||||
|
|
||||||
|
inference-model:
|
||||||
|
build: ./apps/jan-inference-model
|
||||||
|
ports:
|
||||||
|
- "8101:8101"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
### Go Debugging
|
||||||
|
|
||||||
|
For VS Code debugging, add to `.vscode/launch.json`:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Launch Jan API Gateway",
|
||||||
|
"type": "go",
|
||||||
|
"request": "launch",
|
||||||
|
"mode": "auto",
|
||||||
|
"program": "${workspaceFolder}/apps/jan-api-gateway/application/cmd/server",
|
||||||
|
"env": {
|
||||||
|
"JAN_INFERENCE_MODEL_URL": "http://localhost:8101",
|
||||||
|
"JWT_SECRET": "development-secret"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Application Logs
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# View API gateway logs
|
||||||
|
kubectl logs deployment/jan-server-jan-api-gateway -f
|
||||||
|
|
||||||
|
# View inference model logs
|
||||||
|
kubectl logs deployment/jan-server-jan-inference-model -f
|
||||||
|
|
||||||
|
# View PostgreSQL logs
|
||||||
|
kubectl logs statefulset/jan-server-postgresql -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Log Levels
|
||||||
|
|
||||||
|
Set log level via environment variable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export LOG_LEVEL=debug # debug, info, warn, error
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Style and Standards
|
||||||
|
|
||||||
|
### Go Standards
|
||||||
|
|
||||||
|
- Follow [Go Code Review Comments](https://go.dev/wiki/CodeReviewComments)
|
||||||
|
- Use `gofmt` for formatting
|
||||||
|
- Run `go vet` for static analysis
|
||||||
|
- Use meaningful variable and function names
|
||||||
|
|
||||||
|
### API Standards
|
||||||
|
|
||||||
|
- RESTful endpoint design
|
||||||
|
- OpenAPI/Swagger annotations for all endpoints
|
||||||
|
- Consistent error response format
|
||||||
|
- Proper HTTP status codes
|
||||||
|
|
||||||
|
### Git Workflow
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create feature branch
|
||||||
|
git checkout -b feature/your-feature-name
|
||||||
|
|
||||||
|
# Make changes and commit
|
||||||
|
git add .
|
||||||
|
git commit -m "feat: add new authentication endpoint"
|
||||||
|
|
||||||
|
# Push and create PR
|
||||||
|
git push origin feature/your-feature-name
|
||||||
|
```
|
||||||
|
|
||||||
|
### Commit Message Format
|
||||||
|
|
||||||
|
Follow conventional commits:
|
||||||
|
|
||||||
|
```
|
||||||
|
feat: add new feature
|
||||||
|
fix: resolve bug in authentication
|
||||||
|
docs: update API documentation
|
||||||
|
test: add unit tests for service layer
|
||||||
|
refactor: improve error handling
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Testing
|
||||||
|
|
||||||
|
### Load Testing
|
||||||
|
|
||||||
|
Use [k6](https://k6.io) for API load testing:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// load-test.js
|
||||||
|
import http from 'k6/http';
|
||||||
|
|
||||||
|
export default function () {
|
||||||
|
const response = http.post('http://localhost:8080/api/v1/chat/completions', {
|
||||||
|
model: 'jan-v1-4b',
|
||||||
|
messages: [
|
||||||
|
{ role: 'user', content: 'Hello!' }
|
||||||
|
]
|
||||||
|
}, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': 'Bearer your-token'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
check(response, {
|
||||||
|
'status is 200': (r) => r.status === 200,
|
||||||
|
'response time < 5000ms': (r) => r.timings.duration < 5000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Run load test:
|
||||||
|
```bash
|
||||||
|
k6 run --vus 10 --duration 30s load-test.js
|
||||||
|
```
|
||||||
|
|
||||||
|
### Memory Profiling
|
||||||
|
|
||||||
|
Enable Go profiling endpoints:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import _ "net/http/pprof"
|
||||||
|
|
||||||
|
// In main.go
|
||||||
|
go func() {
|
||||||
|
log.Println(http.ListenAndServe("localhost:6060", nil))
|
||||||
|
}()
|
||||||
|
```
|
||||||
|
|
||||||
|
Profile memory usage:
|
||||||
|
```bash
|
||||||
|
go tool pprof http://localhost:6060/debug/pprof/heap
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
### Pull Request Process
|
||||||
|
|
||||||
|
1. **Fork the repository**
|
||||||
|
2. **Create feature branch** from `main`
|
||||||
|
3. **Make changes** following code standards
|
||||||
|
4. **Add tests** for new functionality
|
||||||
|
5. **Update documentation** if needed
|
||||||
|
6. **Submit pull request** with clear description
|
||||||
|
|
||||||
|
### Code Review Checklist
|
||||||
|
|
||||||
|
- [ ] Code follows Go standards
|
||||||
|
- [ ] Tests added for new features
|
||||||
|
- [ ] Documentation updated
|
||||||
|
- [ ] API endpoints have Swagger annotations
|
||||||
|
- [ ] No breaking changes without version bump
|
||||||
|
- [ ] Security considerations addressed
|
||||||
|
|
||||||
|
### Issues and Bug Reports
|
||||||
|
|
||||||
|
When reporting bugs, include:
|
||||||
|
|
||||||
|
- **Environment**: OS, Go version, minikube version
|
||||||
|
- **Steps to reproduce**: Clear, minimal reproduction steps
|
||||||
|
- **Expected behavior**: What should happen
|
||||||
|
- **Actual behavior**: What actually happens
|
||||||
|
- **Logs**: Relevant error messages or logs
|
||||||
|
|
||||||
|
For security issues, please report privately to the maintainers instead of creating public issues.
|
||||||
|
|
||||||
|
## Release Process
|
||||||
|
|
||||||
|
### Version Management
|
||||||
|
|
||||||
|
Jan Server uses semantic versioning (semver):
|
||||||
|
|
||||||
|
- **Major**: Breaking changes
|
||||||
|
- **Minor**: New features, backward compatible
|
||||||
|
- **Patch**: Bug fixes, backward compatible
|
||||||
|
|
||||||
|
### Building Releases
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Tag release
|
||||||
|
git tag -a v1.2.3 -m "Release v1.2.3"
|
||||||
|
|
||||||
|
# Build release images
|
||||||
|
docker build -t jan-api-gateway:v1.2.3 ./apps/jan-api-gateway
|
||||||
|
docker build -t jan-inference-model:v1.2.3 ./apps/jan-inference-model
|
||||||
|
|
||||||
|
# Push tags
|
||||||
|
git push origin v1.2.3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Deployment
|
||||||
|
|
||||||
|
Production deployments follow the same Helm chart structure:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Deploy specific version
|
||||||
|
helm install jan-server ./charts/umbrella-chart \
|
||||||
|
--set jan-api-gateway.image.tag=v1.2.3 \
|
||||||
|
--set jan-inference-model.image.tag=v1.2.3
|
||||||
|
```
|
||||||
39
docs/src/pages/server/index.mdx
Normal file
39
docs/src/pages/server/index.mdx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
---
|
||||||
|
title: Jan Server
|
||||||
|
description: Self-hosted AI infrastructure running the Jan platform on Kubernetes.
|
||||||
|
keywords:
|
||||||
|
[
|
||||||
|
Jan Server,
|
||||||
|
self-hosted AI,
|
||||||
|
Kubernetes deployment,
|
||||||
|
Docker containers,
|
||||||
|
AI inference,
|
||||||
|
local LLM server,
|
||||||
|
VLLM,
|
||||||
|
Go API gateway,
|
||||||
|
Jan-v1 model
|
||||||
|
]
|
||||||
|
---
|
||||||
|
|
||||||
|
## Self-Hosted Jan Platform
|
||||||
|
|
||||||
|
Jan Server deploys the Jan AI platform on your own infrastructure using Kubernetes. It provides a complete AI inference stack with API gateway, model serving, and data persistence.
|
||||||
|
|
||||||
|
Jan Server is in early development. APIs and deployment methods may change.
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
Jan Server consists of two main components:
|
||||||
|
|
||||||
|
- **API Gateway**: Go application handling authentication, web requests, and external integrations
|
||||||
|
- **Inference Model**: VLLM server running the Jan-v1-4B model for AI inference
|
||||||
|
- **PostgreSQL**: Database for user data, conversations, and system state
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
|
||||||
|
- **Kubernetes Native**: Deploys via Helm charts with minikube support
|
||||||
|
- **Jan-v1 Model**: 4B parameter model optimized for reasoning and tool use
|
||||||
|
- **OpenAI Compatible API**: Standard endpoints for integration
|
||||||
|
- **Authentication**: JWT tokens and OAuth2 Google integration
|
||||||
|
- **External Integrations**: Serper API for web search capabilities
|
||||||
|
- **Development Ready**: Local development environment with hot reload
|
||||||
151
docs/src/pages/server/installation.mdx
Normal file
151
docs/src/pages/server/installation.mdx
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
---
|
||||||
|
title: Installation
|
||||||
|
description: Install and deploy Jan Server on Kubernetes using minikube and Helm.
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
Jan Server requires the following tools installed on your system:
|
||||||
|
|
||||||
|
- **Docker**: For building container images
|
||||||
|
- **minikube**: Local Kubernetes cluster for development
|
||||||
|
- **Helm**: Package manager for Kubernetes applications
|
||||||
|
- **kubectl**: Kubernetes command-line tool (installed with minikube)
|
||||||
|
|
||||||
|
Jan Server currently supports minikube for local development. Production Kubernetes deployments are planned for future releases.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
|
||||||
|
1. **Clone the repository**
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/menloresearch/jan-server
|
||||||
|
cd jan-server
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Start minikube**
|
||||||
|
```bash
|
||||||
|
minikube start
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Configure Docker environment**
|
||||||
|
```bash
|
||||||
|
eval $(minikube docker-env)
|
||||||
|
alias kubectl="minikube kubectl --"
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Deploy Jan Server**
|
||||||
|
```bash
|
||||||
|
./scripts/run.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
5. **Access the API**
|
||||||
|
|
||||||
|
The script automatically forwards port 8080. Access the Swagger UI at:
|
||||||
|
```
|
||||||
|
http://localhost:8080/api/swagger/index.html#/
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Manual Installation
|
||||||
|
|
||||||
|
### Build Docker Images
|
||||||
|
|
||||||
|
Build both required Docker images:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build API Gateway
|
||||||
|
docker build -t jan-api-gateway:latest ./apps/jan-api-gateway
|
||||||
|
|
||||||
|
# Build Inference Model
|
||||||
|
docker build -t jan-inference-model:latest ./apps/jan-inference-model
|
||||||
|
```
|
||||||
|
|
||||||
|
The inference model image downloads the Jan-v1-4B model from Hugging Face during build. This requires an internet connection and several GB of download.
|
||||||
|
|
||||||
|
### Deploy with Helm
|
||||||
|
|
||||||
|
Install the Helm chart:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Update Helm dependencies
|
||||||
|
helm dependency update ./charts/umbrella-chart
|
||||||
|
|
||||||
|
# Install Jan Server
|
||||||
|
helm install jan-server ./charts/umbrella-chart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Port Forwarding
|
||||||
|
|
||||||
|
Forward the API gateway port to access from your local machine:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl port-forward svc/jan-server-jan-api-gateway 8080:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verify Installation
|
||||||
|
|
||||||
|
Check that all pods are running:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
kubectl get pods
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected output:
|
||||||
|
```
|
||||||
|
NAME READY STATUS RESTARTS
|
||||||
|
jan-server-jan-api-gateway-xxx 1/1 Running 0
|
||||||
|
jan-server-jan-inference-model-xxx 1/1 Running 0
|
||||||
|
jan-server-postgresql-0 1/1 Running 0
|
||||||
|
```
|
||||||
|
|
||||||
|
Test the API gateway:
|
||||||
|
```bash
|
||||||
|
curl http://localhost:8080/health
|
||||||
|
```
|
||||||
|
|
||||||
|
## Uninstalling
|
||||||
|
|
||||||
|
To remove Jan Server:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
helm uninstall jan-server
|
||||||
|
```
|
||||||
|
|
||||||
|
To stop minikube:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
minikube stop
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Common Issues
|
||||||
|
|
||||||
|
**Pods in `ImagePullBackOff` state**
|
||||||
|
- Ensure Docker images were built in the minikube environment
|
||||||
|
- Run `eval $(minikube docker-env)` before building images
|
||||||
|
|
||||||
|
**Port forwarding connection refused**
|
||||||
|
- Verify the service is running: `kubectl get svc`
|
||||||
|
- Check pod status: `kubectl get pods`
|
||||||
|
- Review logs: `kubectl logs deployment/jan-server-jan-api-gateway`
|
||||||
|
|
||||||
|
**Inference model download fails**
|
||||||
|
- Ensure internet connectivity during Docker build
|
||||||
|
- The Jan-v1-4B model is approximately 2.4GB
|
||||||
|
|
||||||
|
### Resource Requirements
|
||||||
|
|
||||||
|
**Minimum System Requirements:**
|
||||||
|
- 8GB RAM
|
||||||
|
- 20GB free disk space
|
||||||
|
- 4 CPU cores
|
||||||
|
|
||||||
|
**Recommended System Requirements:**
|
||||||
|
- 16GB RAM
|
||||||
|
- 50GB free disk space
|
||||||
|
- 8 CPU cores
|
||||||
|
- GPU support (for faster inference)
|
||||||
|
|
||||||
|
The inference model requires significant memory. Ensure your minikube cluster has adequate resources allocated.
|
||||||
@ -1,25 +1,25 @@
|
|||||||
import React, { Fragment } from 'react'
|
import React, { Fragment } from "react";
|
||||||
import { useConfig, DocsThemeConfig } from 'nextra-theme-docs'
|
import { useConfig, DocsThemeConfig } from "nextra-theme-docs";
|
||||||
import LogoMark from '@/components/LogoMark'
|
import LogoMark from "@/components/LogoMark";
|
||||||
import FooterMenu from '@/components/FooterMenu'
|
import FooterMenu from "@/components/FooterMenu";
|
||||||
import JSONLD from '@/components/JSONLD'
|
import JSONLD from "@/components/JSONLD";
|
||||||
import { useRouter } from 'next/router'
|
import { useRouter } from "next/router";
|
||||||
import Link from 'next/link'
|
import Link from "next/link";
|
||||||
import { LibraryBig, Blocks, BrainCircuit, Computer } from 'lucide-react'
|
import { LibraryBig, Blocks, BrainCircuit, Computer } from "lucide-react";
|
||||||
import { AiOutlineGithub } from 'react-icons/ai'
|
import { AiOutlineGithub } from "react-icons/ai";
|
||||||
import { BiLogoDiscordAlt } from 'react-icons/bi'
|
import { BiLogoDiscordAlt } from "react-icons/bi";
|
||||||
import { RiTwitterXFill } from 'react-icons/ri'
|
import { RiTwitterXFill } from "react-icons/ri";
|
||||||
|
|
||||||
const defaultUrl = 'https://jan.ai'
|
const defaultUrl = "https://jan.ai";
|
||||||
const defaultImage = 'https://jan.ai/assets/images/general/og-image.png'
|
const defaultImage = "https://jan.ai/assets/images/general/og-image.png";
|
||||||
|
|
||||||
const structuredData = {
|
const structuredData = {
|
||||||
'@context': 'https://schema.org',
|
"@context": "https://schema.org",
|
||||||
'@type': 'Organization',
|
"@type": "Organization",
|
||||||
'name': 'Jan',
|
name: "Jan",
|
||||||
'url': `${defaultUrl}`,
|
url: `${defaultUrl}`,
|
||||||
'logo': `${defaultImage}`,
|
logo: `${defaultImage}`,
|
||||||
}
|
};
|
||||||
|
|
||||||
const config: DocsThemeConfig = {
|
const config: DocsThemeConfig = {
|
||||||
logo: (
|
logo: (
|
||||||
@ -30,25 +30,25 @@ const config: DocsThemeConfig = {
|
|||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
),
|
),
|
||||||
docsRepositoryBase: 'https://github.com/menloresearch/jan/tree/dev/docs',
|
docsRepositoryBase: "https://github.com/menloresearch/jan/tree/dev/docs",
|
||||||
feedback: {
|
feedback: {
|
||||||
content: 'Question? Give us feedback →',
|
content: "Question? Give us feedback →",
|
||||||
labels: 'feedback',
|
labels: "feedback",
|
||||||
},
|
},
|
||||||
editLink: {
|
editLink: {
|
||||||
text: 'Edit this page on GitHub →',
|
text: "Edit this page on GitHub →",
|
||||||
},
|
},
|
||||||
useNextSeoProps() {
|
useNextSeoProps() {
|
||||||
return {
|
return {
|
||||||
titleTemplate: '%s - Jan',
|
titleTemplate: "%s - Jan",
|
||||||
twitter: {
|
twitter: {
|
||||||
cardType: 'summary_large_image',
|
cardType: "summary_large_image",
|
||||||
site: '@jandotai',
|
site: "@jandotai",
|
||||||
},
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
type: 'website',
|
type: "website",
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
},
|
},
|
||||||
navbar: {
|
navbar: {
|
||||||
extraContent: (
|
extraContent: (
|
||||||
@ -68,23 +68,21 @@ const config: DocsThemeConfig = {
|
|||||||
sidebar: {
|
sidebar: {
|
||||||
titleComponent: ({ type, title }) => {
|
titleComponent: ({ type, title }) => {
|
||||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||||
const { asPath } = useRouter()
|
const { asPath } = useRouter();
|
||||||
if (type === 'separator' && title === 'Switcher') {
|
if (type === "separator" && title === "Switcher") {
|
||||||
return (
|
return (
|
||||||
<div className="-mx-2 hidden md:block">
|
<div className="-mx-2 hidden md:block">
|
||||||
{[
|
{[
|
||||||
{ title: 'Jan Desktop', path: '/docs', Icon: LibraryBig },
|
|
||||||
{
|
{
|
||||||
title: 'Jan Mobile',
|
title: "Jan Server",
|
||||||
path: '/platforms',
|
path: "/server",
|
||||||
Icon: BrainCircuit,
|
|
||||||
},
|
|
||||||
// { title: 'Jan Mobile', path: '/platforms', Icon: Blocks },
|
|
||||||
{
|
|
||||||
title: 'Jan Server',
|
|
||||||
path: '/platforms',
|
|
||||||
Icon: Computer,
|
Icon: Computer,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Jan Desktop & Mobile",
|
||||||
|
path: "/docs",
|
||||||
|
Icon: LibraryBig,
|
||||||
|
},
|
||||||
].map((item) =>
|
].map((item) =>
|
||||||
asPath.startsWith(item.path) ? (
|
asPath.startsWith(item.path) ? (
|
||||||
<div
|
<div
|
||||||
@ -103,12 +101,12 @@ const config: DocsThemeConfig = {
|
|||||||
<item.Icon className="w-7 h-7 p-1 border rounded border-gray-200 dark:border-gray-700" />
|
<item.Icon className="w-7 h-7 p-1 border rounded border-gray-200 dark:border-gray-700" />
|
||||||
{item.title}
|
{item.title}
|
||||||
</Link>
|
</Link>
|
||||||
)
|
),
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
return title
|
return title;
|
||||||
},
|
},
|
||||||
defaultMenuCollapseLevel: 1,
|
defaultMenuCollapseLevel: 1,
|
||||||
toggleButton: true,
|
toggleButton: true,
|
||||||
@ -117,9 +115,9 @@ const config: DocsThemeConfig = {
|
|||||||
backToTop: true,
|
backToTop: true,
|
||||||
},
|
},
|
||||||
head: function useHead() {
|
head: function useHead() {
|
||||||
const { title, frontMatter } = useConfig()
|
const { title, frontMatter } = useConfig();
|
||||||
const titleTemplate = (frontMatter?.title || title) + ' - ' + 'Jan'
|
const titleTemplate = (frontMatter?.title || title) + " - " + "Jan";
|
||||||
const { asPath } = useRouter()
|
const { asPath } = useRouter();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
@ -143,20 +141,20 @@ const config: DocsThemeConfig = {
|
|||||||
/>
|
/>
|
||||||
<link
|
<link
|
||||||
rel="canonical"
|
rel="canonical"
|
||||||
href={frontMatter?.ogImage ? 'https://jan.ai' + asPath : defaultUrl}
|
href={frontMatter?.ogImage ? "https://jan.ai" + asPath : defaultUrl}
|
||||||
/>
|
/>
|
||||||
<meta
|
<meta
|
||||||
property="og:url"
|
property="og:url"
|
||||||
content={
|
content={
|
||||||
frontMatter?.ogImage ? 'https://jan.ai' + asPath : defaultUrl
|
frontMatter?.ogImage ? "https://jan.ai" + asPath : defaultUrl
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<meta
|
<meta
|
||||||
property="og:image"
|
property="og:image"
|
||||||
content={
|
content={
|
||||||
frontMatter?.ogImage
|
frontMatter?.ogImage
|
||||||
? 'https://jan.ai/' + frontMatter?.ogImage
|
? "https://jan.ai/" + frontMatter?.ogImage
|
||||||
: 'https://jan.ai/assets/images/general/og-image.png'
|
: "https://jan.ai/assets/images/general/og-image.png"
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<meta property="og:image:alt" content="Jan-OGImage" />
|
<meta property="og:image:alt" content="Jan-OGImage" />
|
||||||
@ -164,31 +162,31 @@ const config: DocsThemeConfig = {
|
|||||||
name="keywords"
|
name="keywords"
|
||||||
content={
|
content={
|
||||||
frontMatter?.keywords?.map((keyword: string) => keyword) || [
|
frontMatter?.keywords?.map((keyword: string) => keyword) || [
|
||||||
'Jan',
|
"Jan",
|
||||||
'Customizable Intelligence, LLM',
|
"Customizable Intelligence, LLM",
|
||||||
'local AI',
|
"local AI",
|
||||||
'privacy focus',
|
"privacy focus",
|
||||||
'free and open source',
|
"free and open source",
|
||||||
'private and offline',
|
"private and offline",
|
||||||
'conversational AI',
|
"conversational AI",
|
||||||
'no-subscription fee',
|
"no-subscription fee",
|
||||||
'large language models',
|
"large language models",
|
||||||
'build in public',
|
"build in public",
|
||||||
'remote team',
|
"remote team",
|
||||||
'how we work',
|
"how we work",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<JSONLD data={structuredData} />
|
<JSONLD data={structuredData} />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)
|
);
|
||||||
},
|
},
|
||||||
footer: {
|
footer: {
|
||||||
text: <FooterMenu />,
|
text: <FooterMenu />,
|
||||||
},
|
},
|
||||||
nextThemes: {
|
nextThemes: {
|
||||||
defaultTheme: 'light',
|
defaultTheme: "light",
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
|
|
||||||
export default config
|
export default config;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user