shimmy 2.6.4

Lightweight Ollama-compatible inference server with native SafeTensors support. No Python dependencies, cross-platform WebGPU acceleration via Airframe.
Documentation
use axum::{response::Html, Json};
use serde_json::Value;

const OPENAPI_JSON_TEMPLATE: &str = r##"
{
  "openapi": "3.0.3",
  "info": {
    "title": "Shimmy API",
    "description": "OpenAI-compatible local inference server with native, Ollama-compatible, and Anthropic-compatible endpoints.",
    "version": "VERSION"
  },
  "servers": [{"url": "/"}],
  "paths": {
    "/health": {"get": {"summary": "Health check", "responses": {"200": {"description": "Service health and loaded-model counts."}}}},
    "/metrics": {"get": {"summary": "Runtime metrics", "responses": {"200": {"description": "Runtime and model metrics."}}}},
    "/diag": {"get": {"summary": "Diagnostics", "responses": {"200": {"description": "Diagnostic information."}}}},
    "/api/generate": {"post": {"summary": "Generate text", "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/GenerateRequest"}}}}, "responses": {"200": {"description": "Generated text or an SSE stream."}}}},
    "/api/models": {"get": {"summary": "List native models", "responses": {"200": {"description": "Available models."}}}},
    "/api/models/discover": {"post": {"summary": "Rediscover models", "responses": {"200": {"description": "Discovery result."}}}},
    "/api/models/{name}/load": {"post": {"summary": "Load a model", "parameters": [{"$ref": "#/components/parameters/ModelName"}], "responses": {"200": {"description": "Model load result."}}}},
    "/api/models/{name}/unload": {"post": {"summary": "Unload a model", "parameters": [{"$ref": "#/components/parameters/ModelName"}], "responses": {"200": {"description": "Model unload result."}}}},
    "/api/models/{name}/status": {"get": {"summary": "Get model status", "parameters": [{"$ref": "#/components/parameters/ModelName"}], "responses": {"200": {"description": "Model status."}}}},
    "/api/tools": {"get": {"summary": "List tools", "responses": {"200": {"description": "Available tools."}}}},
    "/api/tools/{name}/execute": {"post": {"summary": "Execute a tool", "parameters": [{"$ref": "#/components/parameters/ToolName"}], "requestBody": {"required": false, "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true}}}}, "responses": {"200": {"description": "Tool result."}}}},
    "/api/workflows/execute": {"post": {"summary": "Execute a workflow", "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true}}}}, "responses": {"200": {"description": "Workflow result."}}}},
    "/v1/models": {"get": {"summary": "List OpenAI-compatible models", "responses": {"200": {"description": "OpenAI model list.", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ModelsResponse"}}}}}}},
    "/v1/chat/completions": {"post": {"summary": "Create a chat completion", "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ChatCompletionRequest"}}}}, "responses": {"200": {"description": "A completion or an SSE stream."}, "400": {"description": "Invalid request."}, "404": {"description": "Model not found."}}}},
    "/v1/completions": {"post": {"summary": "Create a text completion", "requestBody": {"required": true, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/CompletionRequest"}}}}, "responses": {"200": {"description": "A text completion."}}}},
    "/api/tags": {"get": {"summary": "List Ollama-compatible models", "responses": {"200": {"description": "Ollama model list."}}}},
    "/v1/messages": {"post": {"summary": "Create an Anthropic-compatible message", "requestBody": {"required": true, "content": {"application/json": {"schema": {"type": "object", "additionalProperties": true}}}}, "responses": {"200": {"description": "A message response."}}}},
    "/ws/generate": {"get": {"summary": "Generate over WebSocket", "description": "Upgrade this request to a WebSocket connection and exchange GenerateRequest/token frames.", "responses": {"101": {"description": "WebSocket protocol switch."}}}}
  },
  "components": {
    "parameters": {
      "ModelName": {"name": "name", "in": "path", "required": true, "schema": {"type": "string"}},
      "ToolName": {"name": "name", "in": "path", "required": true, "schema": {"type": "string"}}
    },
    "schemas": {
      "GenerateRequest": {"type": "object", "required": ["model", "prompt"], "properties": {"model": {"type": "string"}, "prompt": {"type": "string"}, "messages": {"type": "array", "items": {"type": "object"}}, "system": {"type": "string"}, "temperature": {"type": "number", "format": "float"}, "top_p": {"type": "number", "format": "float"}, "top_k": {"type": "integer"}, "max_tokens": {"type": "integer", "minimum": 1}, "stream": {"type": "boolean"}, "raw_prompt": {"type": "boolean"}}},
      "ChatCompletionRequest": {"type": "object", "required": ["model", "messages"], "properties": {"model": {"type": "string"}, "messages": {"type": "array", "items": {"$ref": "#/components/schemas/ChatMessage"}}, "stream": {"type": "boolean"}, "temperature": {"type": "number", "format": "float"}, "max_tokens": {"type": "integer", "minimum": 1}, "top_p": {"type": "number", "format": "float"}, "stop": {"oneOf": [{"type": "string"}, {"type": "array", "items": {"type": "string"}}]}, "frequency_penalty": {"type": "number", "format": "float"}, "presence_penalty": {"type": "number", "format": "float"}}},
      "ChatMessage": {"type": "object", "required": ["role", "content"], "properties": {"role": {"type": "string"}, "content": {"oneOf": [{"type": "string"}, {"type": "array", "items": {"type": "object"}}]}}},
      "CompletionRequest": {"type": "object", "required": ["model", "prompt"], "properties": {"model": {"type": "string"}, "prompt": {"type": "string"}, "temperature": {"type": "number", "format": "float"}, "max_tokens": {"type": "integer", "minimum": 1}, "top_p": {"type": "number", "format": "float"}, "stream": {"type": "boolean"}}},
      "ModelsResponse": {"type": "object", "required": ["object", "data"], "properties": {"object": {"type": "string", "example": "list"}, "data": {"type": "array", "items": {"type": "object", "required": ["id", "object", "created", "owned_by"], "properties": {"id": {"type": "string"}, "object": {"type": "string"}, "created": {"type": "integer"}, "owned_by": {"type": "string"}}}}}}
    }
  }
}
"##;

pub async fn openapi_json() -> Json<Value> {
    let document =
        OPENAPI_JSON_TEMPLATE.replace("\"VERSION\"", &format!("\"{}\"", env!("CARGO_PKG_VERSION")));
    Json(serde_json::from_str(&document).expect("embedded OpenAPI document must be valid JSON"))
}

pub async fn docs() -> Html<&'static str> {
    Html(
        r##"<!doctype html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>Shimmy API Documentation</title>
    <link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist@5/swagger-ui.css" />
  </head>
  <body>
    <div id="swagger-ui"></div>
    <script src="https://unpkg.com/swagger-ui-dist@5/swagger-ui-bundle.js"></script>
    <script>window.ui = SwaggerUIBundle({ url: "/openapi.json", dom_id: "#swagger-ui" });</script>
  </body>
</html>"##,
    )
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn embedded_spec_is_valid_openapi_document() {
        let document: Value = serde_json::from_str(OPENAPI_JSON_TEMPLATE).unwrap();
        assert_eq!(document["openapi"], "3.0.3");
        assert!(document["paths"]["/v1/chat/completions"].is_object());
        assert!(document["paths"]["/ws/generate"].is_object());
    }

    #[tokio::test]
    async fn docs_page_points_to_openapi_spec() {
        let Html(page) = docs().await;
        assert!(page.contains("swagger-ui-bundle.js"));
        assert!(page.contains("/openapi.json"));
    }
}