V1-API.md 10 KB

/v1 API Documentation

Overview

The /v1 API provides an OpenAI-compatible REST interface for chat completions and model management. It is designed to be a drop-in replacement for the OpenAI API, proxying requests to configured LLM providers (AWS Bedrock, Anthropic, OpenAI-compatible endpoints).

All /v1 endpoints require Bearer token authentication obtained through the OAuth 2.1 authorization flow.

Authentication

Every request to /v1 must include an Authorization header with a valid Bearer access token:

Authorization: Bearer <access_token>

Obtaining a Token

Tokens are issued by the local OAuth 2.1 authorization server. The full flow is:

  1. Register a client

    curl -X POST http://localhost:3000/oauth/register \
     -H "Content-Type: application/json" \
     -d '{"client_name":"my-app","redirect_uris":["http://localhost/callback"]}'
    
  2. Login (sets session cookie)

    curl -X POST http://localhost:3000/oauth/login \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "email=admin@delete.me&password=deleteme"
    
  3. Authorize (user consent)

    curl -G http://localhost:3000/oauth/authorize \
     -b cookies.txt \
     -d "response_type=code" \
     -d "client_id=<client_id>" \
     -d "redirect_uri=http://localhost/callback" \
     -d "scope=llm" \
     -d "code_challenge=<pkce_challenge>" \
     -d "code_challenge_method=S256"
    
  4. Consent

    curl -X POST http://localhost:3000/oauth/consent \
     -b cookies.txt \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "client_id=<client_id>&consent=true"
    
  5. Token exchange

    curl -X POST http://localhost:3000/oauth/token \
     -H "Content-Type: application/x-www-form-urlencoded" \
     -d "grant_type=authorization_code" \
     -d "code=<auth_code>" \
     -d "redirect_uri=http://localhost/callback" \
     -d "client_id=<client_id>" \
     -d "code_verifier=<pkce_verifier>"
    

The response contains access_token which is used for all /v1 requests. Tokens are stored in Redis and validated on every request.

Endpoints

POST /v1/chat/completions

Creates a chat completion for the provided messages using the specified model.

Request Headers

Header Required Description
Authorization Yes Bearer <access_token>
Content-Type Yes application/json

Request Body

Field Type Required Description
model string Yes Model ID in provider/localModelId format (e.g., bedrock/claude-3-sonnet)
messages array Yes Array of message objects {role, content}
stream boolean No If true, returns a Server-Sent Events stream
temperature number No Sampling temperature (0-2)
max_tokens integer No Maximum tokens to generate
top_p number No Nucleus sampling parameter
stop string/array No Stop sequence(s)

Example Request (Non-streaming)

curl -X POST http://localhost:3000/v1/chat/completions \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "bedrock/claude-3-sonnet",
    "messages": [{"role": "user", "content": "Hello!"}],
    "temperature": 0.7,
    "max_tokens": 256
  }'

Example Request (Streaming)

curl -X POST http://localhost:3000/v1/chat/completions \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-3-5-sonnet",
    "messages": [{"role": "user", "content": "Hello!"}],
    "stream": true
  }'

When stream: true, the response is a text/event-stream where each chunk is a JSON object prefixed with data:. The stream ends with data: [DONE].

Response Format (Non-streaming)

{
  "id": "chatcmpl-...",
  "object": "chat.completion",
  "created": 1234567890,
  "model": "bedrock/claude-3-sonnet",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Hello! How can I help you today?"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 10,
    "completion_tokens": 20,
    "total_tokens": 30
  }
}

The exact response shape depends on the underlying provider; the proxy passes through the provider's response after normalizing errors.


GET /v1/models

Lists all available models across enabled providers.

Request Headers

Header Required Description
Authorization Yes Bearer <access_token>

Example Request

curl http://localhost:3000/v1/models \
  -H "Authorization: Bearer <token>"

Response Format

{
  "object": "list",
  "data": [
    {
      "id": "bedrock/claude-3-sonnet",
      "object": "model",
      "created": 1783452810,
      "owned_by": "bedrock"
    },
    {
      "id": "anthropic/claude-3-5-sonnet",
      "object": "model",
      "created": 1783452810,
      "owned_by": "anthropic"
    }
  ]
}

GET /v1/models/{model}

Retrieves details for a single model.

Request Headers

Header Required Description
Authorization Yes Bearer <access_token>

URL Parameter

Parameter Description
model Full model ID. Must URL-encode the slash (e.g., bedrock%2Fclaude-3-sonnet)

Example Request

curl http://localhost:3000/v1/models/bedrock%2Fclaude-3-sonnet \
  -H "Authorization: Bearer <token>"

Response Format

{
  "id": "bedrock/claude-3-sonnet",
  "object": "model",
  "created": 1783452819,
  "owned_by": "bedrock"
}

Important Note on Model IDs

Model IDs use the format provider/localModelId and contain a slash. Because SvelteKit route parameters match a single path segment, the slash must be URL-encoded (%2F) in the request URL:

  • http://localhost:3000/v1/models/bedrock%2Fclaude-3-sonnet
  • http://localhost:3000/v1/models/bedrock/claude-3-sonnet (will 404)

The model IDs returned by GET /v1/models are the raw IDs; clients must encode them before use in the detail endpoint.

Provider Configuration

Providers are configured in config.json under the providers array. Only providers with enabled: true are exposed through the API.

Example config.json

{
  "providers": [
    {
      "id": "bedrock",
      "type": "bedrock",
      "enabled": true,
      "region": "us-east-1",
      "credentials": {
        "accessKeyId": "YOUR_ACCESS_KEY",
        "secretAccessKey": "YOUR_SECRET_KEY"
      },
      "models": {
        "claude-3-sonnet": "anthropic.claude-3-sonnet-20240229-v1:0"
      }
    },
    {
      "id": "anthropic",
      "type": "anthropic",
      "enabled": true,
      "apiKey": "YOUR_ANTHROPIC_API_KEY",
      "models": {
        "claude-3-5-sonnet": "claude-3-5-sonnet-20241022"
      }
    },
    {
      "id": "local",
      "type": "openai-compatible",
      "enabled": false,
      "baseUrl": "http://localhost:11434/v1",
      "models": {
        "llama3.1": "llama3.1:latest"
      }
    }
  ]
}

Provider Types

Type Description Required Config
bedrock AWS Bedrock region, credentials.accessKeyId, credentials.secretAccessKey
anthropic Anthropic API apiKey
openai-compatible Generic OpenAI-compatible endpoint baseUrl

Model ID Format

The models object maps a local model key to the provider's native model identifier:

"models": {
  "claude-3-sonnet": "anthropic.claude-3-sonnet-20240229-v1:0"
}

This model is then addressed via the API as bedrock/claude-3-sonnet.

Error Format

All errors are returned in OpenAI-compatible JSON format:

{
  "error": {
    "message": "Human-readable error description",
    "type": "invalid_request_error",
    "param": null,
    "code": "invalid_model"
  }
}

Error Types

type Meaning
invalid_request_error Bad request (missing params, invalid model, malformed JSON)
invalid_token Authentication failed (bad or expired token)
insufficient_scope Token valid but missing required scope
authentication_error Provider rejected credentials
rate_limit_error Rate limit hit
api_error Provider or internal server error

Common HTTP Status Codes

Status Scenario
200 Success
400 Invalid request (bad JSON, missing model, missing messages, unknown model)
401 Missing or invalid Bearer token
403 Valid token but insufficient scope
404 Model not found
429 Rate limited by provider
500 Internal server error (Redis failure, provider init failure)
502 Provider service error (connection issue, bad credentials, etc.)
503 Provider overloaded

QA Evidence

The following integration tests were executed against http://localhost:3000 with a valid Bearer token seeded in Redis.

Error Case Tests

# Test Expected Actual Result
1 Missing auth header on /v1/models 401 401
2 Invalid token on /v1/models 401 401
3 Invalid model in chat completions 400 400
4 Missing model field 400 400
5 Missing messages field 400 400
6 Invalid JSON body 400 400

Positive Tests

# Test Status Result
7 List models with valid token 200
8 Get model details (URL-encoded slash) 200
9 Get model details (unknown model) 404
10 Chat completions with valid request 502 ✅ (provider rejects placeholder credentials; error correctly translated to OpenAI format)

Server Startup

  • npm run dev started successfully on port 3000
  • No new errors in startup logs
  • Redis connected successfully

Related Documentation