mirror of
https://github.com/lordmathis/llamactl.git
synced 2025-11-05 16:44:22 +00:00
Merge pull request #2 from lordmathis/feat/openai-models
Add OpenAI-compatible endpoints and instance creation timestamp
This commit is contained in:
126
docs/docs.go
126
docs/docs.go
@@ -295,6 +295,45 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"description": "Forwards HTTP requests to the llama-server instance running on a specific port",
|
||||||
|
"tags": [
|
||||||
|
"instances"
|
||||||
|
],
|
||||||
|
"summary": "Proxy requests to a specific instance",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Instance Name",
|
||||||
|
"name": "name",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Request successfully proxied to instance"
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Invalid name format",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"503": {
|
||||||
|
"description": "Instance is not running",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/instances/{name}/restart": {
|
"/instances/{name}/restart": {
|
||||||
@@ -479,6 +518,58 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/v1/": {
|
||||||
|
"post": {
|
||||||
|
"description": "Handles all POST requests to /v1/*, routing to the appropriate instance based on the request body",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"openai"
|
||||||
|
],
|
||||||
|
"summary": "OpenAI-compatible proxy endpoint",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OpenAI response"
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Invalid request body or model name",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/models": {
|
||||||
|
"get": {
|
||||||
|
"description": "Returns a list of instances in a format compatible with OpenAI API",
|
||||||
|
"tags": [
|
||||||
|
"openai"
|
||||||
|
],
|
||||||
|
"summary": "List instances in OpenAI-compatible format",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "List of OpenAI-compatible instances",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/llamactl.OpenAIListInstancesResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
@@ -999,6 +1090,10 @@ const docTemplate = `{
|
|||||||
"llamactl.Instance": {
|
"llamactl.Instance": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"created": {
|
||||||
|
"description": "Creation time",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1007,6 +1102,37 @@ const docTemplate = `{
|
|||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"llamactl.OpenAIInstance": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"created": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"object": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"owned_by": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"llamactl.OpenAIListInstancesResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/llamactl.OpenAIInstance"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"object": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|||||||
@@ -288,6 +288,45 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"post": {
|
||||||
|
"description": "Forwards HTTP requests to the llama-server instance running on a specific port",
|
||||||
|
"tags": [
|
||||||
|
"instances"
|
||||||
|
],
|
||||||
|
"summary": "Proxy requests to a specific instance",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "Instance Name",
|
||||||
|
"name": "name",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Request successfully proxied to instance"
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Invalid name format",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"503": {
|
||||||
|
"description": "Instance is not running",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/instances/{name}/restart": {
|
"/instances/{name}/restart": {
|
||||||
@@ -472,6 +511,58 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/v1/": {
|
||||||
|
"post": {
|
||||||
|
"description": "Handles all POST requests to /v1/*, routing to the appropriate instance based on the request body",
|
||||||
|
"consumes": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"openai"
|
||||||
|
],
|
||||||
|
"summary": "OpenAI-compatible proxy endpoint",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OpenAI response"
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "Invalid request body or model name",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/v1/models": {
|
||||||
|
"get": {
|
||||||
|
"description": "Returns a list of instances in a format compatible with OpenAI API",
|
||||||
|
"tags": [
|
||||||
|
"openai"
|
||||||
|
],
|
||||||
|
"summary": "List instances in OpenAI-compatible format",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "List of OpenAI-compatible instances",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/llamactl.OpenAIListInstancesResponse"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
@@ -992,6 +1083,10 @@
|
|||||||
"llamactl.Instance": {
|
"llamactl.Instance": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"created": {
|
||||||
|
"description": "Creation time",
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -1000,6 +1095,37 @@
|
|||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"llamactl.OpenAIInstance": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"created": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"object": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"owned_by": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"llamactl.OpenAIListInstancesResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/llamactl.OpenAIInstance"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"object": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,12 +347,35 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
llamactl.Instance:
|
llamactl.Instance:
|
||||||
properties:
|
properties:
|
||||||
|
created:
|
||||||
|
description: Creation time
|
||||||
|
type: integer
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
running:
|
running:
|
||||||
description: Status
|
description: Status
|
||||||
type: boolean
|
type: boolean
|
||||||
type: object
|
type: object
|
||||||
|
llamactl.OpenAIInstance:
|
||||||
|
properties:
|
||||||
|
created:
|
||||||
|
type: integer
|
||||||
|
id:
|
||||||
|
type: string
|
||||||
|
object:
|
||||||
|
type: string
|
||||||
|
owned_by:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
llamactl.OpenAIListInstancesResponse:
|
||||||
|
properties:
|
||||||
|
data:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/llamactl.OpenAIInstance'
|
||||||
|
type: array
|
||||||
|
object:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
info:
|
info:
|
||||||
contact: {}
|
contact: {}
|
||||||
description: llamactl is a control server for managing Llama Server instances.
|
description: llamactl is a control server for managing Llama Server instances.
|
||||||
@@ -548,6 +571,33 @@ paths:
|
|||||||
summary: Proxy requests to a specific instance
|
summary: Proxy requests to a specific instance
|
||||||
tags:
|
tags:
|
||||||
- instances
|
- instances
|
||||||
|
post:
|
||||||
|
description: Forwards HTTP requests to the llama-server instance running on
|
||||||
|
a specific port
|
||||||
|
parameters:
|
||||||
|
- description: Instance Name
|
||||||
|
in: path
|
||||||
|
name: name
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: Request successfully proxied to instance
|
||||||
|
"400":
|
||||||
|
description: Invalid name format
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
"503":
|
||||||
|
description: Instance is not running
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
summary: Proxy requests to a specific instance
|
||||||
|
tags:
|
||||||
|
- instances
|
||||||
/instances/{name}/restart:
|
/instances/{name}/restart:
|
||||||
post:
|
post:
|
||||||
description: Restarts a specific instance by name
|
description: Restarts a specific instance by name
|
||||||
@@ -668,4 +718,40 @@ paths:
|
|||||||
summary: Get version of llama server
|
summary: Get version of llama server
|
||||||
tags:
|
tags:
|
||||||
- server
|
- server
|
||||||
|
/v1/:
|
||||||
|
post:
|
||||||
|
consumes:
|
||||||
|
- application/json
|
||||||
|
description: Handles all POST requests to /v1/*, routing to the appropriate
|
||||||
|
instance based on the request body
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OpenAI response
|
||||||
|
"400":
|
||||||
|
description: Invalid request body or model name
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
summary: OpenAI-compatible proxy endpoint
|
||||||
|
tags:
|
||||||
|
- openai
|
||||||
|
/v1/models:
|
||||||
|
get:
|
||||||
|
description: Returns a list of instances in a format compatible with OpenAI
|
||||||
|
API
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: List of OpenAI-compatible instances
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/llamactl.OpenAIListInstancesResponse'
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
type: string
|
||||||
|
summary: List instances in OpenAI-compatible format
|
||||||
|
tags:
|
||||||
|
- openai
|
||||||
swagger: "2.0"
|
swagger: "2.0"
|
||||||
|
|||||||
@@ -402,6 +402,7 @@ func (h *Handler) GetInstanceLogs() http.HandlerFunc {
|
|||||||
// @Failure 500 {string} string "Internal Server Error"
|
// @Failure 500 {string} string "Internal Server Error"
|
||||||
// @Failure 503 {string} string "Instance is not running"
|
// @Failure 503 {string} string "Instance is not running"
|
||||||
// @Router /instances/{name}/proxy [get]
|
// @Router /instances/{name}/proxy [get]
|
||||||
|
// @Router /instances/{name}/proxy [post]
|
||||||
func (h *Handler) ProxyToInstance() http.HandlerFunc {
|
func (h *Handler) ProxyToInstance() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
name := chi.URLParam(r, "name")
|
name := chi.URLParam(r, "name")
|
||||||
@@ -455,7 +456,55 @@ func (h *Handler) ProxyToInstance() http.HandlerFunc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenAIListInstances godoc
|
||||||
|
// @Summary List instances in OpenAI-compatible format
|
||||||
|
// @Description Returns a list of instances in a format compatible with OpenAI API
|
||||||
|
// @Tags openai
|
||||||
|
// @Produces json
|
||||||
|
// @Success 200 {object} OpenAIListInstancesResponse "List of OpenAI-compatible instances"
|
||||||
|
// @Failure 500 {string} string "Internal Server Error"
|
||||||
|
// @Router /v1/models [get]
|
||||||
|
func (h *Handler) OpenAIListInstances() http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
instances, err := h.InstanceManager.ListInstances()
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, "Failed to list instances: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
openaiInstances := make([]OpenAIInstance, len(instances))
|
||||||
|
for i, instance := range instances {
|
||||||
|
openaiInstances[i] = OpenAIInstance{
|
||||||
|
ID: instance.Name,
|
||||||
|
Object: "model",
|
||||||
|
Created: instance.Created,
|
||||||
|
OwnedBy: "llamactl",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openaiResponse := OpenAIListInstancesResponse{
|
||||||
|
Object: "list",
|
||||||
|
Data: openaiInstances,
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
if err := json.NewEncoder(w).Encode(openaiResponse); err != nil {
|
||||||
|
http.Error(w, "Failed to encode instances: "+err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// OpenAIProxy godoc
|
// OpenAIProxy godoc
|
||||||
|
// @Summary OpenAI-compatible proxy endpoint
|
||||||
|
// @Description Handles all POST requests to /v1/*, routing to the appropriate instance based on the request body
|
||||||
|
// @Tags openai
|
||||||
|
// @Accept json
|
||||||
|
// @Produces json
|
||||||
|
// @Success 200 "OpenAI response"
|
||||||
|
// @Failure 400 {string} string "Invalid request body or model name"
|
||||||
|
// @Failure 500 {string} string "Internal Server Error"
|
||||||
|
// @Router /v1/ [post]
|
||||||
func (h *Handler) OpenAIProxy() http.HandlerFunc {
|
func (h *Handler) OpenAIProxy() http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
// Read the entire body first
|
// Read the entire body first
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CreateInstanceOptions struct {
|
type CreateInstanceOptions struct {
|
||||||
@@ -60,6 +61,9 @@ type Instance struct {
|
|||||||
// Status
|
// Status
|
||||||
Running bool `json:"running"`
|
Running bool `json:"running"`
|
||||||
|
|
||||||
|
// Creation time
|
||||||
|
Created int64 `json:"created,omitempty"` // Unix timestamp when the instance was created
|
||||||
|
|
||||||
// Logging file
|
// Logging file
|
||||||
logger *InstanceLogger `json:"-"`
|
logger *InstanceLogger `json:"-"`
|
||||||
|
|
||||||
@@ -153,6 +157,8 @@ func NewInstance(name string, globalSettings *InstancesConfig, options *CreateIn
|
|||||||
logger: logger,
|
logger: logger,
|
||||||
|
|
||||||
Running: false,
|
Running: false,
|
||||||
|
|
||||||
|
Created: time.Now().Unix(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
13
pkg/openai.go
Normal file
13
pkg/openai.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package llamactl
|
||||||
|
|
||||||
|
type OpenAIListInstancesResponse struct {
|
||||||
|
Object string `json:"object"`
|
||||||
|
Data []OpenAIInstance `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OpenAIInstance struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Object string `json:"object"`
|
||||||
|
Created int64 `json:"created"`
|
||||||
|
OwnedBy string `json:"owned_by"`
|
||||||
|
}
|
||||||
@@ -50,6 +50,8 @@ func SetupRouter(handler *Handler) *chi.Mux {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
r.Get(("/v1/models"), handler.OpenAIListInstances()) // List instances in OpenAI-compatible format
|
||||||
|
|
||||||
// OpenAI-compatible proxy endpoint
|
// OpenAI-compatible proxy endpoint
|
||||||
// Handles all POST requests to /v1/*, including:
|
// Handles all POST requests to /v1/*, including:
|
||||||
// - /v1/completions
|
// - /v1/completions
|
||||||
|
|||||||
Reference in New Issue
Block a user