mirror of
https://github.com/meta-llama/llama-stack.git
synced 2025-12-11 19:56:03 +00:00
feat: Add support for dynamically managing provider connections
This commit is contained in:
parent
63422e5b36
commit
d11edf6fee
9 changed files with 3176 additions and 8 deletions
|
|
@ -15,6 +15,120 @@ info:
|
|||
servers:
|
||||
- url: http://any-hosted-llama-stack.com
|
||||
paths:
|
||||
/v1/admin/providers:
|
||||
post:
|
||||
responses:
|
||||
'200':
|
||||
description: >-
|
||||
RegisterProviderResponse with the registered provider info.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegisterProviderResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
'429':
|
||||
$ref: >-
|
||||
#/components/responses/TooManyRequests429
|
||||
'500':
|
||||
$ref: >-
|
||||
#/components/responses/InternalServerError500
|
||||
default:
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
tags:
|
||||
- Providers
|
||||
summary: Register a new dynamic provider.
|
||||
description: >-
|
||||
Register a new dynamic provider.
|
||||
|
||||
Register a new provider instance at runtime. The provider will be validated,
|
||||
|
||||
instantiated, and persisted to the kvstore. Requires appropriate ABAC permissions.
|
||||
parameters: []
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegisterProviderRequest'
|
||||
required: true
|
||||
deprecated: false
|
||||
/v1/admin/providers/{provider_id}:
|
||||
post:
|
||||
responses:
|
||||
'200':
|
||||
description: >-
|
||||
UpdateProviderResponse with updated provider info
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateProviderResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
'429':
|
||||
$ref: >-
|
||||
#/components/responses/TooManyRequests429
|
||||
'500':
|
||||
$ref: >-
|
||||
#/components/responses/InternalServerError500
|
||||
default:
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
tags:
|
||||
- Providers
|
||||
summary: >-
|
||||
Update an existing provider's configuration.
|
||||
description: >-
|
||||
Update an existing provider's configuration.
|
||||
|
||||
Update the configuration and/or attributes of a dynamic provider. The provider
|
||||
|
||||
will be re-instantiated with the new configuration (hot-reload). Static providers
|
||||
|
||||
from run.yaml cannot be updated.
|
||||
parameters:
|
||||
- name: provider_id
|
||||
in: path
|
||||
description: ID of the provider to update
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateProviderRequest'
|
||||
required: true
|
||||
deprecated: false
|
||||
delete:
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
'429':
|
||||
$ref: >-
|
||||
#/components/responses/TooManyRequests429
|
||||
'500':
|
||||
$ref: >-
|
||||
#/components/responses/InternalServerError500
|
||||
default:
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
tags:
|
||||
- Providers
|
||||
summary: Unregister a dynamic provider.
|
||||
description: >-
|
||||
Unregister a dynamic provider.
|
||||
|
||||
Remove a dynamic provider, shutting down its instance and removing it from
|
||||
|
||||
the kvstore. Static providers from run.yaml cannot be unregistered.
|
||||
parameters:
|
||||
- name: provider_id
|
||||
in: path
|
||||
description: ID of the provider to unregister.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
deprecated: false
|
||||
/v1/chat/completions:
|
||||
get:
|
||||
responses:
|
||||
|
|
@ -1289,6 +1403,43 @@ paths:
|
|||
schema:
|
||||
type: string
|
||||
deprecated: false
|
||||
/v1/providers/{provider_id}/test:
|
||||
post:
|
||||
responses:
|
||||
'200':
|
||||
description: >-
|
||||
TestProviderConnectionResponse with health status.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TestProviderConnectionResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
'429':
|
||||
$ref: >-
|
||||
#/components/responses/TooManyRequests429
|
||||
'500':
|
||||
$ref: >-
|
||||
#/components/responses/InternalServerError500
|
||||
default:
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
tags:
|
||||
- Providers
|
||||
summary: Test a provider connection.
|
||||
description: >-
|
||||
Test a provider connection.
|
||||
|
||||
Execute a health check on a provider to verify it is reachable and functioning.
|
||||
|
||||
Works for both static and dynamic providers.
|
||||
parameters:
|
||||
- name: provider_id
|
||||
in: path
|
||||
description: ID of the provider to test.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
deprecated: false
|
||||
/v1/responses:
|
||||
get:
|
||||
responses:
|
||||
|
|
@ -4212,6 +4363,251 @@ components:
|
|||
title: Error
|
||||
description: >-
|
||||
Error response from the API. Roughly follows RFC 7807.
|
||||
RegisterProviderRequest:
|
||||
type: object
|
||||
properties:
|
||||
provider_id:
|
||||
type: string
|
||||
description: >-
|
||||
Unique identifier for this provider instance.
|
||||
api:
|
||||
type: string
|
||||
description: API namespace this provider implements.
|
||||
provider_type:
|
||||
type: string
|
||||
description: Provider type (e.g., 'remote::openai').
|
||||
config:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: >-
|
||||
Provider configuration (API keys, endpoints, etc.).
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: >-
|
||||
Optional attributes for ABAC access control.
|
||||
additionalProperties: false
|
||||
required:
|
||||
- provider_id
|
||||
- api
|
||||
- provider_type
|
||||
- config
|
||||
title: RegisterProviderRequest
|
||||
ProviderConnectionInfo:
|
||||
type: object
|
||||
properties:
|
||||
provider_id:
|
||||
type: string
|
||||
description: >-
|
||||
Unique identifier for this provider instance
|
||||
api:
|
||||
type: string
|
||||
description: >-
|
||||
API namespace (e.g., "inference", "vector_io", "safety")
|
||||
provider_type:
|
||||
type: string
|
||||
description: >-
|
||||
Provider type identifier (e.g., "remote::openai", "inline::faiss")
|
||||
config:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: >-
|
||||
Provider-specific configuration (API keys, endpoints, etc.)
|
||||
status:
|
||||
$ref: '#/components/schemas/ProviderConnectionStatus'
|
||||
description: Current connection status
|
||||
health:
|
||||
$ref: '#/components/schemas/ProviderHealth'
|
||||
description: Most recent health check result
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp when provider was registered
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp of last update
|
||||
last_health_check:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp of last health check
|
||||
error_message:
|
||||
type: string
|
||||
description: Error message if status is failed
|
||||
metadata:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: >-
|
||||
User-defined metadata (deprecated, use attributes)
|
||||
owner:
|
||||
type: object
|
||||
properties:
|
||||
principal:
|
||||
type: string
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
additionalProperties: false
|
||||
required:
|
||||
- principal
|
||||
description: >-
|
||||
User who created this provider connection
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: >-
|
||||
Key-value attributes for ABAC access control
|
||||
additionalProperties: false
|
||||
required:
|
||||
- provider_id
|
||||
- api
|
||||
- provider_type
|
||||
- config
|
||||
- status
|
||||
- created_at
|
||||
- updated_at
|
||||
- metadata
|
||||
title: ProviderConnectionInfo
|
||||
description: >-
|
||||
Information about a dynamically managed provider connection.
|
||||
|
||||
This model represents a provider that has been registered at runtime
|
||||
|
||||
via the /providers API, as opposed to static providers configured in run.yaml.
|
||||
|
||||
|
||||
Dynamic providers support full lifecycle management including registration,
|
||||
|
||||
configuration updates, health monitoring, and removal.
|
||||
ProviderConnectionStatus:
|
||||
type: string
|
||||
enum:
|
||||
- pending
|
||||
- initializing
|
||||
- connected
|
||||
- failed
|
||||
- disconnected
|
||||
- testing
|
||||
title: ProviderConnectionStatus
|
||||
description: Status of a dynamic provider connection.
|
||||
ProviderHealth:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum:
|
||||
- OK
|
||||
- Error
|
||||
- Not Implemented
|
||||
description: >-
|
||||
Health status (OK, ERROR, NOT_IMPLEMENTED)
|
||||
message:
|
||||
type: string
|
||||
description: Optional error or status message
|
||||
metrics:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: Provider-specific health metrics
|
||||
last_checked:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp of last health check
|
||||
additionalProperties: false
|
||||
required:
|
||||
- status
|
||||
- metrics
|
||||
- last_checked
|
||||
title: ProviderHealth
|
||||
description: >-
|
||||
Structured wrapper around provider health status.
|
||||
|
||||
This wraps the existing dict-based HealthResponse for API responses
|
||||
|
||||
while maintaining backward compatibility with existing provider implementations.
|
||||
RegisterProviderResponse:
|
||||
type: object
|
||||
properties:
|
||||
provider:
|
||||
$ref: '#/components/schemas/ProviderConnectionInfo'
|
||||
description: >-
|
||||
Information about the registered provider
|
||||
additionalProperties: false
|
||||
required:
|
||||
- provider
|
||||
title: RegisterProviderResponse
|
||||
description: Response after registering a provider.
|
||||
UpdateProviderRequest:
|
||||
type: object
|
||||
properties:
|
||||
config:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: >-
|
||||
New configuration parameters (merged with existing)
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: New attributes for access control
|
||||
additionalProperties: false
|
||||
title: UpdateProviderRequest
|
||||
UpdateProviderResponse:
|
||||
type: object
|
||||
properties:
|
||||
provider:
|
||||
$ref: '#/components/schemas/ProviderConnectionInfo'
|
||||
description: Updated provider information
|
||||
additionalProperties: false
|
||||
required:
|
||||
- provider
|
||||
title: UpdateProviderResponse
|
||||
description: Response after updating a provider.
|
||||
Order:
|
||||
type: string
|
||||
enum:
|
||||
|
|
@ -6680,6 +7076,32 @@ components:
|
|||
title: ListProvidersResponse
|
||||
description: >-
|
||||
Response containing a list of all available providers.
|
||||
TestProviderConnectionResponse:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
description: Whether the connection test succeeded
|
||||
health:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: Health status from the provider
|
||||
error_message:
|
||||
type: string
|
||||
description: Error message if test failed
|
||||
additionalProperties: false
|
||||
required:
|
||||
- success
|
||||
title: TestProviderConnectionResponse
|
||||
description: >-
|
||||
Response from testing a provider connection.
|
||||
ListOpenAIResponseObject:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
|||
571
docs/static/llama-stack-spec.html
vendored
571
docs/static/llama-stack-spec.html
vendored
|
|
@ -40,6 +40,142 @@
|
|||
}
|
||||
],
|
||||
"paths": {
|
||||
"/v1/admin/providers": {
|
||||
"post": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "RegisterProviderResponse with the registered provider info.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RegisterProviderResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/components/responses/BadRequest400"
|
||||
},
|
||||
"429": {
|
||||
"$ref": "#/components/responses/TooManyRequests429"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/components/responses/InternalServerError500"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/components/responses/DefaultError"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Providers"
|
||||
],
|
||||
"summary": "Register a new dynamic provider.",
|
||||
"description": "Register a new dynamic provider.\nRegister a new provider instance at runtime. The provider will be validated,\ninstantiated, and persisted to the kvstore. Requires appropriate ABAC permissions.",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RegisterProviderRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"/v1/admin/providers/{provider_id}": {
|
||||
"post": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "UpdateProviderResponse with updated provider info",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpdateProviderResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/components/responses/BadRequest400"
|
||||
},
|
||||
"429": {
|
||||
"$ref": "#/components/responses/TooManyRequests429"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/components/responses/InternalServerError500"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/components/responses/DefaultError"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Providers"
|
||||
],
|
||||
"summary": "Update an existing provider's configuration.",
|
||||
"description": "Update an existing provider's configuration.\nUpdate the configuration and/or attributes of a dynamic provider. The provider\nwill be re-instantiated with the new configuration (hot-reload). Static providers\nfrom run.yaml cannot be updated.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "provider_id",
|
||||
"in": "path",
|
||||
"description": "ID of the provider to update",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpdateProviderRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"delete": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/components/responses/BadRequest400"
|
||||
},
|
||||
"429": {
|
||||
"$ref": "#/components/responses/TooManyRequests429"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/components/responses/InternalServerError500"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/components/responses/DefaultError"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Providers"
|
||||
],
|
||||
"summary": "Unregister a dynamic provider.",
|
||||
"description": "Unregister a dynamic provider.\nRemove a dynamic provider, shutting down its instance and removing it from\nthe kvstore. Static providers from run.yaml cannot be unregistered.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "provider_id",
|
||||
"in": "path",
|
||||
"description": "ID of the provider to unregister.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"/v1/chat/completions": {
|
||||
"get": {
|
||||
"responses": {
|
||||
|
|
@ -1680,6 +1816,51 @@
|
|||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"/v1/providers/{provider_id}/test": {
|
||||
"post": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "TestProviderConnectionResponse with health status.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestProviderConnectionResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/components/responses/BadRequest400"
|
||||
},
|
||||
"429": {
|
||||
"$ref": "#/components/responses/TooManyRequests429"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/components/responses/InternalServerError500"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/components/responses/DefaultError"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Providers"
|
||||
],
|
||||
"summary": "Test a provider connection.",
|
||||
"description": "Test a provider connection.\nExecute a health check on a provider to verify it is reachable and functioning.\nWorks for both static and dynamic providers.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "provider_id",
|
||||
"in": "path",
|
||||
"description": "ID of the provider to test.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"/v1/responses": {
|
||||
"get": {
|
||||
"responses": {
|
||||
|
|
@ -4005,6 +4186,351 @@
|
|||
"title": "Error",
|
||||
"description": "Error response from the API. Roughly follows RFC 7807."
|
||||
},
|
||||
"RegisterProviderRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"provider_id": {
|
||||
"type": "string",
|
||||
"description": "Unique identifier for this provider instance."
|
||||
},
|
||||
"api": {
|
||||
"type": "string",
|
||||
"description": "API namespace this provider implements."
|
||||
},
|
||||
"provider_type": {
|
||||
"type": "string",
|
||||
"description": "Provider type (e.g., 'remote::openai')."
|
||||
},
|
||||
"config": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Provider configuration (API keys, endpoints, etc.)."
|
||||
},
|
||||
"attributes": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": "Optional attributes for ABAC access control."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"provider_id",
|
||||
"api",
|
||||
"provider_type",
|
||||
"config"
|
||||
],
|
||||
"title": "RegisterProviderRequest"
|
||||
},
|
||||
"ProviderConnectionInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"provider_id": {
|
||||
"type": "string",
|
||||
"description": "Unique identifier for this provider instance"
|
||||
},
|
||||
"api": {
|
||||
"type": "string",
|
||||
"description": "API namespace (e.g., \"inference\", \"vector_io\", \"safety\")"
|
||||
},
|
||||
"provider_type": {
|
||||
"type": "string",
|
||||
"description": "Provider type identifier (e.g., \"remote::openai\", \"inline::faiss\")"
|
||||
},
|
||||
"config": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Provider-specific configuration (API keys, endpoints, etc.)"
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/components/schemas/ProviderConnectionStatus",
|
||||
"description": "Current connection status"
|
||||
},
|
||||
"health": {
|
||||
"$ref": "#/components/schemas/ProviderHealth",
|
||||
"description": "Most recent health check result"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Timestamp when provider was registered"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Timestamp of last update"
|
||||
},
|
||||
"last_health_check": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Timestamp of last health check"
|
||||
},
|
||||
"error_message": {
|
||||
"type": "string",
|
||||
"description": "Error message if status is failed"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "User-defined metadata (deprecated, use attributes)"
|
||||
},
|
||||
"owner": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"principal": {
|
||||
"type": "string"
|
||||
},
|
||||
"attributes": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"principal"
|
||||
],
|
||||
"description": "User who created this provider connection"
|
||||
},
|
||||
"attributes": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": "Key-value attributes for ABAC access control"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"provider_id",
|
||||
"api",
|
||||
"provider_type",
|
||||
"config",
|
||||
"status",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"metadata"
|
||||
],
|
||||
"title": "ProviderConnectionInfo",
|
||||
"description": "Information about a dynamically managed provider connection.\nThis model represents a provider that has been registered at runtime\nvia the /providers API, as opposed to static providers configured in run.yaml.\n\nDynamic providers support full lifecycle management including registration,\nconfiguration updates, health monitoring, and removal."
|
||||
},
|
||||
"ProviderConnectionStatus": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"pending",
|
||||
"initializing",
|
||||
"connected",
|
||||
"failed",
|
||||
"disconnected",
|
||||
"testing"
|
||||
],
|
||||
"title": "ProviderConnectionStatus",
|
||||
"description": "Status of a dynamic provider connection."
|
||||
},
|
||||
"ProviderHealth": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"OK",
|
||||
"Error",
|
||||
"Not Implemented"
|
||||
],
|
||||
"description": "Health status (OK, ERROR, NOT_IMPLEMENTED)"
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "Optional error or status message"
|
||||
},
|
||||
"metrics": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Provider-specific health metrics"
|
||||
},
|
||||
"last_checked": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Timestamp of last health check"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"status",
|
||||
"metrics",
|
||||
"last_checked"
|
||||
],
|
||||
"title": "ProviderHealth",
|
||||
"description": "Structured wrapper around provider health status.\nThis wraps the existing dict-based HealthResponse for API responses\nwhile maintaining backward compatibility with existing provider implementations."
|
||||
},
|
||||
"RegisterProviderResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"provider": {
|
||||
"$ref": "#/components/schemas/ProviderConnectionInfo",
|
||||
"description": "Information about the registered provider"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"provider"
|
||||
],
|
||||
"title": "RegisterProviderResponse",
|
||||
"description": "Response after registering a provider."
|
||||
},
|
||||
"UpdateProviderRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "New configuration parameters (merged with existing)"
|
||||
},
|
||||
"attributes": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": "New attributes for access control"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"title": "UpdateProviderRequest"
|
||||
},
|
||||
"UpdateProviderResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"provider": {
|
||||
"$ref": "#/components/schemas/ProviderConnectionInfo",
|
||||
"description": "Updated provider information"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"provider"
|
||||
],
|
||||
"title": "UpdateProviderResponse",
|
||||
"description": "Response after updating a provider."
|
||||
},
|
||||
"Order": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
|
@ -7242,6 +7768,51 @@
|
|||
"title": "ListProvidersResponse",
|
||||
"description": "Response containing a list of all available providers."
|
||||
},
|
||||
"TestProviderConnectionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the connection test succeeded"
|
||||
},
|
||||
"health": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Health status from the provider"
|
||||
},
|
||||
"error_message": {
|
||||
"type": "string",
|
||||
"description": "Error message if test failed"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"success"
|
||||
],
|
||||
"title": "TestProviderConnectionResponse",
|
||||
"description": "Response from testing a provider connection."
|
||||
},
|
||||
"ListOpenAIResponseObject": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
|||
422
docs/static/llama-stack-spec.yaml
vendored
422
docs/static/llama-stack-spec.yaml
vendored
|
|
@ -12,6 +12,120 @@ info:
|
|||
servers:
|
||||
- url: http://any-hosted-llama-stack.com
|
||||
paths:
|
||||
/v1/admin/providers:
|
||||
post:
|
||||
responses:
|
||||
'200':
|
||||
description: >-
|
||||
RegisterProviderResponse with the registered provider info.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegisterProviderResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
'429':
|
||||
$ref: >-
|
||||
#/components/responses/TooManyRequests429
|
||||
'500':
|
||||
$ref: >-
|
||||
#/components/responses/InternalServerError500
|
||||
default:
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
tags:
|
||||
- Providers
|
||||
summary: Register a new dynamic provider.
|
||||
description: >-
|
||||
Register a new dynamic provider.
|
||||
|
||||
Register a new provider instance at runtime. The provider will be validated,
|
||||
|
||||
instantiated, and persisted to the kvstore. Requires appropriate ABAC permissions.
|
||||
parameters: []
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegisterProviderRequest'
|
||||
required: true
|
||||
deprecated: false
|
||||
/v1/admin/providers/{provider_id}:
|
||||
post:
|
||||
responses:
|
||||
'200':
|
||||
description: >-
|
||||
UpdateProviderResponse with updated provider info
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateProviderResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
'429':
|
||||
$ref: >-
|
||||
#/components/responses/TooManyRequests429
|
||||
'500':
|
||||
$ref: >-
|
||||
#/components/responses/InternalServerError500
|
||||
default:
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
tags:
|
||||
- Providers
|
||||
summary: >-
|
||||
Update an existing provider's configuration.
|
||||
description: >-
|
||||
Update an existing provider's configuration.
|
||||
|
||||
Update the configuration and/or attributes of a dynamic provider. The provider
|
||||
|
||||
will be re-instantiated with the new configuration (hot-reload). Static providers
|
||||
|
||||
from run.yaml cannot be updated.
|
||||
parameters:
|
||||
- name: provider_id
|
||||
in: path
|
||||
description: ID of the provider to update
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateProviderRequest'
|
||||
required: true
|
||||
deprecated: false
|
||||
delete:
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
'429':
|
||||
$ref: >-
|
||||
#/components/responses/TooManyRequests429
|
||||
'500':
|
||||
$ref: >-
|
||||
#/components/responses/InternalServerError500
|
||||
default:
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
tags:
|
||||
- Providers
|
||||
summary: Unregister a dynamic provider.
|
||||
description: >-
|
||||
Unregister a dynamic provider.
|
||||
|
||||
Remove a dynamic provider, shutting down its instance and removing it from
|
||||
|
||||
the kvstore. Static providers from run.yaml cannot be unregistered.
|
||||
parameters:
|
||||
- name: provider_id
|
||||
in: path
|
||||
description: ID of the provider to unregister.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
deprecated: false
|
||||
/v1/chat/completions:
|
||||
get:
|
||||
responses:
|
||||
|
|
@ -1286,6 +1400,43 @@ paths:
|
|||
schema:
|
||||
type: string
|
||||
deprecated: false
|
||||
/v1/providers/{provider_id}/test:
|
||||
post:
|
||||
responses:
|
||||
'200':
|
||||
description: >-
|
||||
TestProviderConnectionResponse with health status.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TestProviderConnectionResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
'429':
|
||||
$ref: >-
|
||||
#/components/responses/TooManyRequests429
|
||||
'500':
|
||||
$ref: >-
|
||||
#/components/responses/InternalServerError500
|
||||
default:
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
tags:
|
||||
- Providers
|
||||
summary: Test a provider connection.
|
||||
description: >-
|
||||
Test a provider connection.
|
||||
|
||||
Execute a health check on a provider to verify it is reachable and functioning.
|
||||
|
||||
Works for both static and dynamic providers.
|
||||
parameters:
|
||||
- name: provider_id
|
||||
in: path
|
||||
description: ID of the provider to test.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
deprecated: false
|
||||
/v1/responses:
|
||||
get:
|
||||
responses:
|
||||
|
|
@ -2999,6 +3150,251 @@ components:
|
|||
title: Error
|
||||
description: >-
|
||||
Error response from the API. Roughly follows RFC 7807.
|
||||
RegisterProviderRequest:
|
||||
type: object
|
||||
properties:
|
||||
provider_id:
|
||||
type: string
|
||||
description: >-
|
||||
Unique identifier for this provider instance.
|
||||
api:
|
||||
type: string
|
||||
description: API namespace this provider implements.
|
||||
provider_type:
|
||||
type: string
|
||||
description: Provider type (e.g., 'remote::openai').
|
||||
config:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: >-
|
||||
Provider configuration (API keys, endpoints, etc.).
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: >-
|
||||
Optional attributes for ABAC access control.
|
||||
additionalProperties: false
|
||||
required:
|
||||
- provider_id
|
||||
- api
|
||||
- provider_type
|
||||
- config
|
||||
title: RegisterProviderRequest
|
||||
ProviderConnectionInfo:
|
||||
type: object
|
||||
properties:
|
||||
provider_id:
|
||||
type: string
|
||||
description: >-
|
||||
Unique identifier for this provider instance
|
||||
api:
|
||||
type: string
|
||||
description: >-
|
||||
API namespace (e.g., "inference", "vector_io", "safety")
|
||||
provider_type:
|
||||
type: string
|
||||
description: >-
|
||||
Provider type identifier (e.g., "remote::openai", "inline::faiss")
|
||||
config:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: >-
|
||||
Provider-specific configuration (API keys, endpoints, etc.)
|
||||
status:
|
||||
$ref: '#/components/schemas/ProviderConnectionStatus'
|
||||
description: Current connection status
|
||||
health:
|
||||
$ref: '#/components/schemas/ProviderHealth'
|
||||
description: Most recent health check result
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp when provider was registered
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp of last update
|
||||
last_health_check:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp of last health check
|
||||
error_message:
|
||||
type: string
|
||||
description: Error message if status is failed
|
||||
metadata:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: >-
|
||||
User-defined metadata (deprecated, use attributes)
|
||||
owner:
|
||||
type: object
|
||||
properties:
|
||||
principal:
|
||||
type: string
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
additionalProperties: false
|
||||
required:
|
||||
- principal
|
||||
description: >-
|
||||
User who created this provider connection
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: >-
|
||||
Key-value attributes for ABAC access control
|
||||
additionalProperties: false
|
||||
required:
|
||||
- provider_id
|
||||
- api
|
||||
- provider_type
|
||||
- config
|
||||
- status
|
||||
- created_at
|
||||
- updated_at
|
||||
- metadata
|
||||
title: ProviderConnectionInfo
|
||||
description: >-
|
||||
Information about a dynamically managed provider connection.
|
||||
|
||||
This model represents a provider that has been registered at runtime
|
||||
|
||||
via the /providers API, as opposed to static providers configured in run.yaml.
|
||||
|
||||
|
||||
Dynamic providers support full lifecycle management including registration,
|
||||
|
||||
configuration updates, health monitoring, and removal.
|
||||
ProviderConnectionStatus:
|
||||
type: string
|
||||
enum:
|
||||
- pending
|
||||
- initializing
|
||||
- connected
|
||||
- failed
|
||||
- disconnected
|
||||
- testing
|
||||
title: ProviderConnectionStatus
|
||||
description: Status of a dynamic provider connection.
|
||||
ProviderHealth:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum:
|
||||
- OK
|
||||
- Error
|
||||
- Not Implemented
|
||||
description: >-
|
||||
Health status (OK, ERROR, NOT_IMPLEMENTED)
|
||||
message:
|
||||
type: string
|
||||
description: Optional error or status message
|
||||
metrics:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: Provider-specific health metrics
|
||||
last_checked:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp of last health check
|
||||
additionalProperties: false
|
||||
required:
|
||||
- status
|
||||
- metrics
|
||||
- last_checked
|
||||
title: ProviderHealth
|
||||
description: >-
|
||||
Structured wrapper around provider health status.
|
||||
|
||||
This wraps the existing dict-based HealthResponse for API responses
|
||||
|
||||
while maintaining backward compatibility with existing provider implementations.
|
||||
RegisterProviderResponse:
|
||||
type: object
|
||||
properties:
|
||||
provider:
|
||||
$ref: '#/components/schemas/ProviderConnectionInfo'
|
||||
description: >-
|
||||
Information about the registered provider
|
||||
additionalProperties: false
|
||||
required:
|
||||
- provider
|
||||
title: RegisterProviderResponse
|
||||
description: Response after registering a provider.
|
||||
UpdateProviderRequest:
|
||||
type: object
|
||||
properties:
|
||||
config:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: >-
|
||||
New configuration parameters (merged with existing)
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: New attributes for access control
|
||||
additionalProperties: false
|
||||
title: UpdateProviderRequest
|
||||
UpdateProviderResponse:
|
||||
type: object
|
||||
properties:
|
||||
provider:
|
||||
$ref: '#/components/schemas/ProviderConnectionInfo'
|
||||
description: Updated provider information
|
||||
additionalProperties: false
|
||||
required:
|
||||
- provider
|
||||
title: UpdateProviderResponse
|
||||
description: Response after updating a provider.
|
||||
Order:
|
||||
type: string
|
||||
enum:
|
||||
|
|
@ -5467,6 +5863,32 @@ components:
|
|||
title: ListProvidersResponse
|
||||
description: >-
|
||||
Response containing a list of all available providers.
|
||||
TestProviderConnectionResponse:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
description: Whether the connection test succeeded
|
||||
health:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: Health status from the provider
|
||||
error_message:
|
||||
type: string
|
||||
description: Error message if test failed
|
||||
additionalProperties: false
|
||||
required:
|
||||
- success
|
||||
title: TestProviderConnectionResponse
|
||||
description: >-
|
||||
Response from testing a provider connection.
|
||||
ListOpenAIResponseObject:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
|||
571
docs/static/stainless-llama-stack-spec.html
vendored
571
docs/static/stainless-llama-stack-spec.html
vendored
|
|
@ -40,6 +40,142 @@
|
|||
}
|
||||
],
|
||||
"paths": {
|
||||
"/v1/admin/providers": {
|
||||
"post": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "RegisterProviderResponse with the registered provider info.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RegisterProviderResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/components/responses/BadRequest400"
|
||||
},
|
||||
"429": {
|
||||
"$ref": "#/components/responses/TooManyRequests429"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/components/responses/InternalServerError500"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/components/responses/DefaultError"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Providers"
|
||||
],
|
||||
"summary": "Register a new dynamic provider.",
|
||||
"description": "Register a new dynamic provider.\nRegister a new provider instance at runtime. The provider will be validated,\ninstantiated, and persisted to the kvstore. Requires appropriate ABAC permissions.",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RegisterProviderRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"/v1/admin/providers/{provider_id}": {
|
||||
"post": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "UpdateProviderResponse with updated provider info",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpdateProviderResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/components/responses/BadRequest400"
|
||||
},
|
||||
"429": {
|
||||
"$ref": "#/components/responses/TooManyRequests429"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/components/responses/InternalServerError500"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/components/responses/DefaultError"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Providers"
|
||||
],
|
||||
"summary": "Update an existing provider's configuration.",
|
||||
"description": "Update an existing provider's configuration.\nUpdate the configuration and/or attributes of a dynamic provider. The provider\nwill be re-instantiated with the new configuration (hot-reload). Static providers\nfrom run.yaml cannot be updated.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "provider_id",
|
||||
"in": "path",
|
||||
"description": "ID of the provider to update",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/UpdateProviderRequest"
|
||||
}
|
||||
}
|
||||
},
|
||||
"required": true
|
||||
},
|
||||
"deprecated": false
|
||||
},
|
||||
"delete": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK"
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/components/responses/BadRequest400"
|
||||
},
|
||||
"429": {
|
||||
"$ref": "#/components/responses/TooManyRequests429"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/components/responses/InternalServerError500"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/components/responses/DefaultError"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Providers"
|
||||
],
|
||||
"summary": "Unregister a dynamic provider.",
|
||||
"description": "Unregister a dynamic provider.\nRemove a dynamic provider, shutting down its instance and removing it from\nthe kvstore. Static providers from run.yaml cannot be unregistered.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "provider_id",
|
||||
"in": "path",
|
||||
"description": "ID of the provider to unregister.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"/v1/chat/completions": {
|
||||
"get": {
|
||||
"responses": {
|
||||
|
|
@ -1680,6 +1816,51 @@
|
|||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"/v1/providers/{provider_id}/test": {
|
||||
"post": {
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "TestProviderConnectionResponse with health status.",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TestProviderConnectionResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/components/responses/BadRequest400"
|
||||
},
|
||||
"429": {
|
||||
"$ref": "#/components/responses/TooManyRequests429"
|
||||
},
|
||||
"500": {
|
||||
"$ref": "#/components/responses/InternalServerError500"
|
||||
},
|
||||
"default": {
|
||||
"$ref": "#/components/responses/DefaultError"
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Providers"
|
||||
],
|
||||
"summary": "Test a provider connection.",
|
||||
"description": "Test a provider connection.\nExecute a health check on a provider to verify it is reachable and functioning.\nWorks for both static and dynamic providers.",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "provider_id",
|
||||
"in": "path",
|
||||
"description": "ID of the provider to test.",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
],
|
||||
"deprecated": false
|
||||
}
|
||||
},
|
||||
"/v1/responses": {
|
||||
"get": {
|
||||
"responses": {
|
||||
|
|
@ -5677,6 +5858,351 @@
|
|||
"title": "Error",
|
||||
"description": "Error response from the API. Roughly follows RFC 7807."
|
||||
},
|
||||
"RegisterProviderRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"provider_id": {
|
||||
"type": "string",
|
||||
"description": "Unique identifier for this provider instance."
|
||||
},
|
||||
"api": {
|
||||
"type": "string",
|
||||
"description": "API namespace this provider implements."
|
||||
},
|
||||
"provider_type": {
|
||||
"type": "string",
|
||||
"description": "Provider type (e.g., 'remote::openai')."
|
||||
},
|
||||
"config": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Provider configuration (API keys, endpoints, etc.)."
|
||||
},
|
||||
"attributes": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": "Optional attributes for ABAC access control."
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"provider_id",
|
||||
"api",
|
||||
"provider_type",
|
||||
"config"
|
||||
],
|
||||
"title": "RegisterProviderRequest"
|
||||
},
|
||||
"ProviderConnectionInfo": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"provider_id": {
|
||||
"type": "string",
|
||||
"description": "Unique identifier for this provider instance"
|
||||
},
|
||||
"api": {
|
||||
"type": "string",
|
||||
"description": "API namespace (e.g., \"inference\", \"vector_io\", \"safety\")"
|
||||
},
|
||||
"provider_type": {
|
||||
"type": "string",
|
||||
"description": "Provider type identifier (e.g., \"remote::openai\", \"inline::faiss\")"
|
||||
},
|
||||
"config": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Provider-specific configuration (API keys, endpoints, etc.)"
|
||||
},
|
||||
"status": {
|
||||
"$ref": "#/components/schemas/ProviderConnectionStatus",
|
||||
"description": "Current connection status"
|
||||
},
|
||||
"health": {
|
||||
"$ref": "#/components/schemas/ProviderHealth",
|
||||
"description": "Most recent health check result"
|
||||
},
|
||||
"created_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Timestamp when provider was registered"
|
||||
},
|
||||
"updated_at": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Timestamp of last update"
|
||||
},
|
||||
"last_health_check": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Timestamp of last health check"
|
||||
},
|
||||
"error_message": {
|
||||
"type": "string",
|
||||
"description": "Error message if status is failed"
|
||||
},
|
||||
"metadata": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "User-defined metadata (deprecated, use attributes)"
|
||||
},
|
||||
"owner": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"principal": {
|
||||
"type": "string"
|
||||
},
|
||||
"attributes": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"principal"
|
||||
],
|
||||
"description": "User who created this provider connection"
|
||||
},
|
||||
"attributes": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": "Key-value attributes for ABAC access control"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"provider_id",
|
||||
"api",
|
||||
"provider_type",
|
||||
"config",
|
||||
"status",
|
||||
"created_at",
|
||||
"updated_at",
|
||||
"metadata"
|
||||
],
|
||||
"title": "ProviderConnectionInfo",
|
||||
"description": "Information about a dynamically managed provider connection.\nThis model represents a provider that has been registered at runtime\nvia the /providers API, as opposed to static providers configured in run.yaml.\n\nDynamic providers support full lifecycle management including registration,\nconfiguration updates, health monitoring, and removal."
|
||||
},
|
||||
"ProviderConnectionStatus": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"pending",
|
||||
"initializing",
|
||||
"connected",
|
||||
"failed",
|
||||
"disconnected",
|
||||
"testing"
|
||||
],
|
||||
"title": "ProviderConnectionStatus",
|
||||
"description": "Status of a dynamic provider connection."
|
||||
},
|
||||
"ProviderHealth": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"status": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"OK",
|
||||
"Error",
|
||||
"Not Implemented"
|
||||
],
|
||||
"description": "Health status (OK, ERROR, NOT_IMPLEMENTED)"
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
"description": "Optional error or status message"
|
||||
},
|
||||
"metrics": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Provider-specific health metrics"
|
||||
},
|
||||
"last_checked": {
|
||||
"type": "string",
|
||||
"format": "date-time",
|
||||
"description": "Timestamp of last health check"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"status",
|
||||
"metrics",
|
||||
"last_checked"
|
||||
],
|
||||
"title": "ProviderHealth",
|
||||
"description": "Structured wrapper around provider health status.\nThis wraps the existing dict-based HealthResponse for API responses\nwhile maintaining backward compatibility with existing provider implementations."
|
||||
},
|
||||
"RegisterProviderResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"provider": {
|
||||
"$ref": "#/components/schemas/ProviderConnectionInfo",
|
||||
"description": "Information about the registered provider"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"provider"
|
||||
],
|
||||
"title": "RegisterProviderResponse",
|
||||
"description": "Response after registering a provider."
|
||||
},
|
||||
"UpdateProviderRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"config": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "New configuration parameters (merged with existing)"
|
||||
},
|
||||
"attributes": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"description": "New attributes for access control"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"title": "UpdateProviderRequest"
|
||||
},
|
||||
"UpdateProviderResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"provider": {
|
||||
"$ref": "#/components/schemas/ProviderConnectionInfo",
|
||||
"description": "Updated provider information"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"provider"
|
||||
],
|
||||
"title": "UpdateProviderResponse",
|
||||
"description": "Response after updating a provider."
|
||||
},
|
||||
"Order": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
|
@ -8914,6 +9440,51 @@
|
|||
"title": "ListProvidersResponse",
|
||||
"description": "Response containing a list of all available providers."
|
||||
},
|
||||
"TestProviderConnectionResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"success": {
|
||||
"type": "boolean",
|
||||
"description": "Whether the connection test succeeded"
|
||||
},
|
||||
"health": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "null"
|
||||
},
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
},
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "array"
|
||||
},
|
||||
{
|
||||
"type": "object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Health status from the provider"
|
||||
},
|
||||
"error_message": {
|
||||
"type": "string",
|
||||
"description": "Error message if test failed"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"success"
|
||||
],
|
||||
"title": "TestProviderConnectionResponse",
|
||||
"description": "Response from testing a provider connection."
|
||||
},
|
||||
"ListOpenAIResponseObject": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
|
|
|
|||
422
docs/static/stainless-llama-stack-spec.yaml
vendored
422
docs/static/stainless-llama-stack-spec.yaml
vendored
|
|
@ -15,6 +15,120 @@ info:
|
|||
servers:
|
||||
- url: http://any-hosted-llama-stack.com
|
||||
paths:
|
||||
/v1/admin/providers:
|
||||
post:
|
||||
responses:
|
||||
'200':
|
||||
description: >-
|
||||
RegisterProviderResponse with the registered provider info.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegisterProviderResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
'429':
|
||||
$ref: >-
|
||||
#/components/responses/TooManyRequests429
|
||||
'500':
|
||||
$ref: >-
|
||||
#/components/responses/InternalServerError500
|
||||
default:
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
tags:
|
||||
- Providers
|
||||
summary: Register a new dynamic provider.
|
||||
description: >-
|
||||
Register a new dynamic provider.
|
||||
|
||||
Register a new provider instance at runtime. The provider will be validated,
|
||||
|
||||
instantiated, and persisted to the kvstore. Requires appropriate ABAC permissions.
|
||||
parameters: []
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RegisterProviderRequest'
|
||||
required: true
|
||||
deprecated: false
|
||||
/v1/admin/providers/{provider_id}:
|
||||
post:
|
||||
responses:
|
||||
'200':
|
||||
description: >-
|
||||
UpdateProviderResponse with updated provider info
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateProviderResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
'429':
|
||||
$ref: >-
|
||||
#/components/responses/TooManyRequests429
|
||||
'500':
|
||||
$ref: >-
|
||||
#/components/responses/InternalServerError500
|
||||
default:
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
tags:
|
||||
- Providers
|
||||
summary: >-
|
||||
Update an existing provider's configuration.
|
||||
description: >-
|
||||
Update an existing provider's configuration.
|
||||
|
||||
Update the configuration and/or attributes of a dynamic provider. The provider
|
||||
|
||||
will be re-instantiated with the new configuration (hot-reload). Static providers
|
||||
|
||||
from run.yaml cannot be updated.
|
||||
parameters:
|
||||
- name: provider_id
|
||||
in: path
|
||||
description: ID of the provider to update
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/UpdateProviderRequest'
|
||||
required: true
|
||||
deprecated: false
|
||||
delete:
|
||||
responses:
|
||||
'200':
|
||||
description: OK
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
'429':
|
||||
$ref: >-
|
||||
#/components/responses/TooManyRequests429
|
||||
'500':
|
||||
$ref: >-
|
||||
#/components/responses/InternalServerError500
|
||||
default:
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
tags:
|
||||
- Providers
|
||||
summary: Unregister a dynamic provider.
|
||||
description: >-
|
||||
Unregister a dynamic provider.
|
||||
|
||||
Remove a dynamic provider, shutting down its instance and removing it from
|
||||
|
||||
the kvstore. Static providers from run.yaml cannot be unregistered.
|
||||
parameters:
|
||||
- name: provider_id
|
||||
in: path
|
||||
description: ID of the provider to unregister.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
deprecated: false
|
||||
/v1/chat/completions:
|
||||
get:
|
||||
responses:
|
||||
|
|
@ -1289,6 +1403,43 @@ paths:
|
|||
schema:
|
||||
type: string
|
||||
deprecated: false
|
||||
/v1/providers/{provider_id}/test:
|
||||
post:
|
||||
responses:
|
||||
'200':
|
||||
description: >-
|
||||
TestProviderConnectionResponse with health status.
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/TestProviderConnectionResponse'
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest400'
|
||||
'429':
|
||||
$ref: >-
|
||||
#/components/responses/TooManyRequests429
|
||||
'500':
|
||||
$ref: >-
|
||||
#/components/responses/InternalServerError500
|
||||
default:
|
||||
$ref: '#/components/responses/DefaultError'
|
||||
tags:
|
||||
- Providers
|
||||
summary: Test a provider connection.
|
||||
description: >-
|
||||
Test a provider connection.
|
||||
|
||||
Execute a health check on a provider to verify it is reachable and functioning.
|
||||
|
||||
Works for both static and dynamic providers.
|
||||
parameters:
|
||||
- name: provider_id
|
||||
in: path
|
||||
description: ID of the provider to test.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
deprecated: false
|
||||
/v1/responses:
|
||||
get:
|
||||
responses:
|
||||
|
|
@ -4212,6 +4363,251 @@ components:
|
|||
title: Error
|
||||
description: >-
|
||||
Error response from the API. Roughly follows RFC 7807.
|
||||
RegisterProviderRequest:
|
||||
type: object
|
||||
properties:
|
||||
provider_id:
|
||||
type: string
|
||||
description: >-
|
||||
Unique identifier for this provider instance.
|
||||
api:
|
||||
type: string
|
||||
description: API namespace this provider implements.
|
||||
provider_type:
|
||||
type: string
|
||||
description: Provider type (e.g., 'remote::openai').
|
||||
config:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: >-
|
||||
Provider configuration (API keys, endpoints, etc.).
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: >-
|
||||
Optional attributes for ABAC access control.
|
||||
additionalProperties: false
|
||||
required:
|
||||
- provider_id
|
||||
- api
|
||||
- provider_type
|
||||
- config
|
||||
title: RegisterProviderRequest
|
||||
ProviderConnectionInfo:
|
||||
type: object
|
||||
properties:
|
||||
provider_id:
|
||||
type: string
|
||||
description: >-
|
||||
Unique identifier for this provider instance
|
||||
api:
|
||||
type: string
|
||||
description: >-
|
||||
API namespace (e.g., "inference", "vector_io", "safety")
|
||||
provider_type:
|
||||
type: string
|
||||
description: >-
|
||||
Provider type identifier (e.g., "remote::openai", "inline::faiss")
|
||||
config:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: >-
|
||||
Provider-specific configuration (API keys, endpoints, etc.)
|
||||
status:
|
||||
$ref: '#/components/schemas/ProviderConnectionStatus'
|
||||
description: Current connection status
|
||||
health:
|
||||
$ref: '#/components/schemas/ProviderHealth'
|
||||
description: Most recent health check result
|
||||
created_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp when provider was registered
|
||||
updated_at:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp of last update
|
||||
last_health_check:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp of last health check
|
||||
error_message:
|
||||
type: string
|
||||
description: Error message if status is failed
|
||||
metadata:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: >-
|
||||
User-defined metadata (deprecated, use attributes)
|
||||
owner:
|
||||
type: object
|
||||
properties:
|
||||
principal:
|
||||
type: string
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
additionalProperties: false
|
||||
required:
|
||||
- principal
|
||||
description: >-
|
||||
User who created this provider connection
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: >-
|
||||
Key-value attributes for ABAC access control
|
||||
additionalProperties: false
|
||||
required:
|
||||
- provider_id
|
||||
- api
|
||||
- provider_type
|
||||
- config
|
||||
- status
|
||||
- created_at
|
||||
- updated_at
|
||||
- metadata
|
||||
title: ProviderConnectionInfo
|
||||
description: >-
|
||||
Information about a dynamically managed provider connection.
|
||||
|
||||
This model represents a provider that has been registered at runtime
|
||||
|
||||
via the /providers API, as opposed to static providers configured in run.yaml.
|
||||
|
||||
|
||||
Dynamic providers support full lifecycle management including registration,
|
||||
|
||||
configuration updates, health monitoring, and removal.
|
||||
ProviderConnectionStatus:
|
||||
type: string
|
||||
enum:
|
||||
- pending
|
||||
- initializing
|
||||
- connected
|
||||
- failed
|
||||
- disconnected
|
||||
- testing
|
||||
title: ProviderConnectionStatus
|
||||
description: Status of a dynamic provider connection.
|
||||
ProviderHealth:
|
||||
type: object
|
||||
properties:
|
||||
status:
|
||||
type: string
|
||||
enum:
|
||||
- OK
|
||||
- Error
|
||||
- Not Implemented
|
||||
description: >-
|
||||
Health status (OK, ERROR, NOT_IMPLEMENTED)
|
||||
message:
|
||||
type: string
|
||||
description: Optional error or status message
|
||||
metrics:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: Provider-specific health metrics
|
||||
last_checked:
|
||||
type: string
|
||||
format: date-time
|
||||
description: Timestamp of last health check
|
||||
additionalProperties: false
|
||||
required:
|
||||
- status
|
||||
- metrics
|
||||
- last_checked
|
||||
title: ProviderHealth
|
||||
description: >-
|
||||
Structured wrapper around provider health status.
|
||||
|
||||
This wraps the existing dict-based HealthResponse for API responses
|
||||
|
||||
while maintaining backward compatibility with existing provider implementations.
|
||||
RegisterProviderResponse:
|
||||
type: object
|
||||
properties:
|
||||
provider:
|
||||
$ref: '#/components/schemas/ProviderConnectionInfo'
|
||||
description: >-
|
||||
Information about the registered provider
|
||||
additionalProperties: false
|
||||
required:
|
||||
- provider
|
||||
title: RegisterProviderResponse
|
||||
description: Response after registering a provider.
|
||||
UpdateProviderRequest:
|
||||
type: object
|
||||
properties:
|
||||
config:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: >-
|
||||
New configuration parameters (merged with existing)
|
||||
attributes:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: New attributes for access control
|
||||
additionalProperties: false
|
||||
title: UpdateProviderRequest
|
||||
UpdateProviderResponse:
|
||||
type: object
|
||||
properties:
|
||||
provider:
|
||||
$ref: '#/components/schemas/ProviderConnectionInfo'
|
||||
description: Updated provider information
|
||||
additionalProperties: false
|
||||
required:
|
||||
- provider
|
||||
title: UpdateProviderResponse
|
||||
description: Response after updating a provider.
|
||||
Order:
|
||||
type: string
|
||||
enum:
|
||||
|
|
@ -6680,6 +7076,32 @@ components:
|
|||
title: ListProvidersResponse
|
||||
description: >-
|
||||
Response containing a list of all available providers.
|
||||
TestProviderConnectionResponse:
|
||||
type: object
|
||||
properties:
|
||||
success:
|
||||
type: boolean
|
||||
description: Whether the connection test succeeded
|
||||
health:
|
||||
type: object
|
||||
additionalProperties:
|
||||
oneOf:
|
||||
- type: 'null'
|
||||
- type: boolean
|
||||
- type: number
|
||||
- type: string
|
||||
- type: array
|
||||
- type: object
|
||||
description: Health status from the provider
|
||||
error_message:
|
||||
type: string
|
||||
description: Error message if test failed
|
||||
additionalProperties: false
|
||||
required:
|
||||
- success
|
||||
title: TestProviderConnectionResponse
|
||||
description: >-
|
||||
Response from testing a provider connection.
|
||||
ListOpenAIResponseObject:
|
||||
type: object
|
||||
properties:
|
||||
|
|
|
|||
117
llama_stack/apis/providers/connection.py
Normal file
117
llama_stack/apis/providers/connection.py
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||
# All rights reserved.
|
||||
#
|
||||
# This source code is licensed under the terms described in the LICENSE file in
|
||||
# the root directory of this source tree.
|
||||
|
||||
from datetime import UTC, datetime
|
||||
from enum import StrEnum
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from llama_stack.core.datatypes import User
|
||||
from llama_stack.providers.datatypes import HealthStatus
|
||||
from llama_stack.schema_utils import json_schema_type
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class ProviderConnectionStatus(StrEnum):
|
||||
"""Status of a dynamic provider connection.
|
||||
|
||||
:cvar pending: Configuration stored, not yet initialized
|
||||
:cvar initializing: In the process of connecting
|
||||
:cvar connected: Successfully connected and healthy
|
||||
:cvar failed: Connection attempt failed
|
||||
:cvar disconnected: Previously connected, now disconnected
|
||||
:cvar testing: Health check in progress
|
||||
"""
|
||||
|
||||
pending = "pending"
|
||||
initializing = "initializing"
|
||||
connected = "connected"
|
||||
failed = "failed"
|
||||
disconnected = "disconnected"
|
||||
testing = "testing"
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class ProviderHealth(BaseModel):
|
||||
"""Structured wrapper around provider health status.
|
||||
|
||||
This wraps the existing dict-based HealthResponse for API responses
|
||||
while maintaining backward compatibility with existing provider implementations.
|
||||
|
||||
:param status: Health status (OK, ERROR, NOT_IMPLEMENTED)
|
||||
:param message: Optional error or status message
|
||||
:param metrics: Provider-specific health metrics
|
||||
:param last_checked: Timestamp of last health check
|
||||
"""
|
||||
|
||||
status: HealthStatus
|
||||
message: str | None = None
|
||||
metrics: dict[str, Any] = Field(default_factory=dict)
|
||||
last_checked: datetime
|
||||
|
||||
@classmethod
|
||||
def from_health_response(cls, response: dict[str, Any]) -> "ProviderHealth":
|
||||
"""Convert dict-based HealthResponse to ProviderHealth.
|
||||
|
||||
This allows us to maintain the existing dict[str, Any] return type
|
||||
for provider.health() methods while providing a structured model
|
||||
for API responses.
|
||||
|
||||
:param response: Dict with 'status' and optional 'message', 'metrics'
|
||||
:returns: ProviderHealth instance
|
||||
"""
|
||||
return cls(
|
||||
status=HealthStatus(response.get("status", HealthStatus.NOT_IMPLEMENTED)),
|
||||
message=response.get("message"),
|
||||
metrics=response.get("metrics", {}),
|
||||
last_checked=datetime.now(UTC),
|
||||
)
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class ProviderConnectionInfo(BaseModel):
|
||||
"""Information about a dynamically managed provider connection.
|
||||
|
||||
This model represents a provider that has been registered at runtime
|
||||
via the /providers API, as opposed to static providers configured in run.yaml.
|
||||
|
||||
Dynamic providers support full lifecycle management including registration,
|
||||
configuration updates, health monitoring, and removal.
|
||||
|
||||
:param provider_id: Unique identifier for this provider instance
|
||||
:param api: API namespace (e.g., "inference", "vector_io", "safety")
|
||||
:param provider_type: Provider type identifier (e.g., "remote::openai", "inline::faiss")
|
||||
:param config: Provider-specific configuration (API keys, endpoints, etc.)
|
||||
:param status: Current connection status
|
||||
:param health: Most recent health check result
|
||||
:param created_at: Timestamp when provider was registered
|
||||
:param updated_at: Timestamp of last update
|
||||
:param last_health_check: Timestamp of last health check
|
||||
:param error_message: Error message if status is failed
|
||||
:param metadata: User-defined metadata (deprecated, use attributes)
|
||||
:param owner: User who created this provider connection
|
||||
:param attributes: Key-value attributes for ABAC access control
|
||||
"""
|
||||
|
||||
provider_id: str
|
||||
api: str
|
||||
provider_type: str
|
||||
config: dict[str, Any]
|
||||
status: ProviderConnectionStatus
|
||||
health: ProviderHealth | None = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
last_health_check: datetime | None = None
|
||||
error_message: str | None = None
|
||||
metadata: dict[str, Any] = Field(
|
||||
default_factory=dict,
|
||||
description="Deprecated: use attributes for access control",
|
||||
)
|
||||
|
||||
# ABAC fields (same as ResourceWithOwner)
|
||||
owner: User | None = None
|
||||
attributes: dict[str, list[str]] | None = None
|
||||
|
|
@ -8,6 +8,7 @@ from typing import Any, Protocol, runtime_checkable
|
|||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from llama_stack.apis.providers.connection import ProviderConnectionInfo
|
||||
from llama_stack.apis.version import LLAMA_STACK_API_V1
|
||||
from llama_stack.providers.datatypes import HealthResponse
|
||||
from llama_stack.schema_utils import json_schema_type, webmethod
|
||||
|
|
@ -40,6 +41,85 @@ class ListProvidersResponse(BaseModel):
|
|||
data: list[ProviderInfo]
|
||||
|
||||
|
||||
# ===== Dynamic Provider Management API Models =====
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class RegisterProviderRequest(BaseModel):
|
||||
"""Request to register a new dynamic provider.
|
||||
|
||||
:param provider_id: Unique identifier for the provider instance
|
||||
:param api: API namespace (e.g., 'inference', 'vector_io', 'safety')
|
||||
:param provider_type: Provider type identifier (e.g., 'remote::openai', 'inline::faiss')
|
||||
:param config: Provider-specific configuration (API keys, endpoints, etc.)
|
||||
:param attributes: Optional key-value attributes for ABAC access control
|
||||
"""
|
||||
|
||||
provider_id: str
|
||||
api: str
|
||||
provider_type: str
|
||||
config: dict[str, Any]
|
||||
attributes: dict[str, list[str]] | None = None
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class RegisterProviderResponse(BaseModel):
|
||||
"""Response after registering a provider.
|
||||
|
||||
:param provider: Information about the registered provider
|
||||
"""
|
||||
|
||||
provider: ProviderConnectionInfo
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class UpdateProviderRequest(BaseModel):
|
||||
"""Request to update an existing provider's configuration.
|
||||
|
||||
:param config: New configuration parameters (will be merged with existing)
|
||||
:param attributes: Optional updated attributes for access control
|
||||
"""
|
||||
|
||||
config: dict[str, Any] | None = None
|
||||
attributes: dict[str, list[str]] | None = None
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class UpdateProviderResponse(BaseModel):
|
||||
"""Response after updating a provider.
|
||||
|
||||
:param provider: Updated provider information
|
||||
"""
|
||||
|
||||
provider: ProviderConnectionInfo
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class UnregisterProviderResponse(BaseModel):
|
||||
"""Response after unregistering a provider.
|
||||
|
||||
:param success: Whether the operation succeeded
|
||||
:param message: Optional status message
|
||||
"""
|
||||
|
||||
success: bool
|
||||
message: str | None = None
|
||||
|
||||
|
||||
@json_schema_type
|
||||
class TestProviderConnectionResponse(BaseModel):
|
||||
"""Response from testing a provider connection.
|
||||
|
||||
:param success: Whether the connection test succeeded
|
||||
:param health: Health status from the provider
|
||||
:param error_message: Error message if test failed
|
||||
"""
|
||||
|
||||
success: bool
|
||||
health: HealthResponse | None = None
|
||||
error_message: str | None = None
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class Providers(Protocol):
|
||||
"""Providers
|
||||
|
|
@ -67,3 +147,71 @@ class Providers(Protocol):
|
|||
:returns: A ProviderInfo object containing the provider's details.
|
||||
"""
|
||||
...
|
||||
|
||||
# ===== Dynamic Provider Management Methods =====
|
||||
|
||||
@webmethod(route="/admin/providers", method="POST", level=LLAMA_STACK_API_V1)
|
||||
async def register_provider(
|
||||
self,
|
||||
provider_id: str,
|
||||
api: str,
|
||||
provider_type: str,
|
||||
config: dict[str, Any],
|
||||
attributes: dict[str, list[str]] | None = None,
|
||||
) -> RegisterProviderResponse:
|
||||
"""Register a new dynamic provider.
|
||||
|
||||
Register a new provider instance at runtime. The provider will be validated,
|
||||
instantiated, and persisted to the kvstore. Requires appropriate ABAC permissions.
|
||||
|
||||
:param provider_id: Unique identifier for this provider instance.
|
||||
:param api: API namespace this provider implements.
|
||||
:param provider_type: Provider type (e.g., 'remote::openai').
|
||||
:param config: Provider configuration (API keys, endpoints, etc.).
|
||||
:param attributes: Optional attributes for ABAC access control.
|
||||
:returns: RegisterProviderResponse with the registered provider info.
|
||||
"""
|
||||
...
|
||||
|
||||
@webmethod(route="/admin/providers/{provider_id}", method="PUT", level=LLAMA_STACK_API_V1)
|
||||
async def update_provider(
|
||||
self,
|
||||
provider_id: str,
|
||||
config: dict[str, Any] | None = None,
|
||||
attributes: dict[str, list[str]] | None = None,
|
||||
) -> UpdateProviderResponse:
|
||||
"""Update an existing provider's configuration.
|
||||
|
||||
Update the configuration and/or attributes of a dynamic provider. The provider
|
||||
will be re-instantiated with the new configuration (hot-reload). Static providers
|
||||
from run.yaml cannot be updated.
|
||||
|
||||
:param provider_id: ID of the provider to update
|
||||
:param config: New configuration parameters (merged with existing)
|
||||
:param attributes: New attributes for access control
|
||||
:returns: UpdateProviderResponse with updated provider info
|
||||
"""
|
||||
...
|
||||
|
||||
@webmethod(route="/admin/providers/{provider_id}", method="DELETE", level=LLAMA_STACK_API_V1)
|
||||
async def unregister_provider(self, provider_id: str) -> None:
|
||||
"""Unregister a dynamic provider.
|
||||
|
||||
Remove a dynamic provider, shutting down its instance and removing it from
|
||||
the kvstore. Static providers from run.yaml cannot be unregistered.
|
||||
|
||||
:param provider_id: ID of the provider to unregister.
|
||||
"""
|
||||
...
|
||||
|
||||
@webmethod(route="/providers/{provider_id}/test", method="POST", level=LLAMA_STACK_API_V1)
|
||||
async def test_provider_connection(self, provider_id: str) -> TestProviderConnectionResponse:
|
||||
"""Test a provider connection.
|
||||
|
||||
Execute a health check on a provider to verify it is reachable and functioning.
|
||||
Works for both static and dynamic providers.
|
||||
|
||||
:param provider_id: ID of the provider to test.
|
||||
:returns: TestProviderConnectionResponse with health status.
|
||||
"""
|
||||
...
|
||||
|
|
|
|||
|
|
@ -5,22 +5,43 @@
|
|||
# the root directory of this source tree.
|
||||
|
||||
import asyncio
|
||||
from datetime import UTC, datetime
|
||||
from typing import Any
|
||||
|
||||
from pydantic import BaseModel
|
||||
|
||||
from llama_stack.apis.providers import ListProvidersResponse, ProviderInfo, Providers
|
||||
from llama_stack.apis.providers import (
|
||||
ListProvidersResponse,
|
||||
ProviderInfo,
|
||||
Providers,
|
||||
RegisterProviderResponse,
|
||||
TestProviderConnectionResponse,
|
||||
UpdateProviderResponse,
|
||||
)
|
||||
from llama_stack.apis.providers.connection import (
|
||||
ProviderConnectionInfo,
|
||||
ProviderConnectionStatus,
|
||||
ProviderHealth,
|
||||
)
|
||||
from llama_stack.core.request_headers import get_authenticated_user
|
||||
from llama_stack.core.resolver import ProviderWithSpec, instantiate_provider
|
||||
from llama_stack.log import get_logger
|
||||
from llama_stack.providers.datatypes import HealthResponse, HealthStatus
|
||||
from llama_stack.providers.datatypes import Api, HealthResponse, HealthStatus
|
||||
|
||||
from .datatypes import StackRunConfig
|
||||
from .utils.config import redact_sensitive_fields
|
||||
|
||||
logger = get_logger(name=__name__, category="core")
|
||||
|
||||
# Storage constants for dynamic provider connections
|
||||
PROVIDER_CONNECTIONS_PREFIX = "provider_connections:v1::"
|
||||
|
||||
|
||||
class ProviderImplConfig(BaseModel):
|
||||
run_config: StackRunConfig
|
||||
provider_registry: Any | None = None # ProviderRegistry from resolver
|
||||
dist_registry: Any | None = None # DistributionRegistry
|
||||
policy: list[Any] | None = None # list[AccessRule]
|
||||
|
||||
|
||||
async def get_provider_impl(config, deps):
|
||||
|
|
@ -33,19 +54,71 @@ class ProviderImpl(Providers):
|
|||
def __init__(self, config, deps):
|
||||
self.config = config
|
||||
self.deps = deps
|
||||
self.kvstore = None # KVStore for dynamic provider persistence
|
||||
self.dynamic_providers: dict[str, ProviderConnectionInfo] = {} # Runtime cache
|
||||
self.dynamic_provider_impls: dict[str, Any] = {} # Initialized provider instances
|
||||
|
||||
# Store registry references for provider instantiation
|
||||
self.provider_registry = config.provider_registry
|
||||
self.dist_registry = config.dist_registry
|
||||
self.policy = config.policy or []
|
||||
|
||||
async def initialize(self) -> None:
|
||||
pass
|
||||
# Initialize kvstore for dynamic providers
|
||||
# Reuse the same kvstore as the distribution registry if available
|
||||
if hasattr(self.config.run_config, "metadata_store") and self.config.run_config.metadata_store:
|
||||
from llama_stack.providers.utils.kvstore import kvstore_impl
|
||||
|
||||
self.kvstore = await kvstore_impl(self.config.run_config.metadata_store)
|
||||
logger.info("Initialized kvstore for dynamic provider management")
|
||||
|
||||
# Load existing dynamic providers from kvstore
|
||||
await self._load_dynamic_providers()
|
||||
logger.info(f"Loaded {len(self.dynamic_providers)} dynamic providers from kvstore")
|
||||
|
||||
# Auto-instantiate connected providers on startup
|
||||
if self.provider_registry:
|
||||
for provider_id, conn_info in self.dynamic_providers.items():
|
||||
if conn_info.status == ProviderConnectionStatus.connected:
|
||||
try:
|
||||
impl = await self._instantiate_provider(conn_info)
|
||||
self.dynamic_provider_impls[provider_id] = impl
|
||||
logger.info(f"Auto-instantiated provider {provider_id} from kvstore")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to auto-instantiate provider {provider_id}: {e}")
|
||||
# Update status to failed
|
||||
conn_info.status = ProviderConnectionStatus.failed
|
||||
conn_info.error_message = str(e)
|
||||
conn_info.updated_at = datetime.now(UTC)
|
||||
await self._store_connection(conn_info)
|
||||
else:
|
||||
logger.warning("Provider registry not available, skipping auto-instantiation")
|
||||
else:
|
||||
logger.warning("No metadata_store configured, dynamic provider management disabled")
|
||||
|
||||
async def shutdown(self) -> None:
|
||||
logger.debug("ProviderImpl.shutdown")
|
||||
pass
|
||||
|
||||
# Shutdown all dynamic provider instances
|
||||
for provider_id, impl in self.dynamic_provider_impls.items():
|
||||
try:
|
||||
if hasattr(impl, "shutdown"):
|
||||
await impl.shutdown()
|
||||
logger.debug(f"Shutdown dynamic provider {provider_id}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Error shutting down dynamic provider {provider_id}: {e}")
|
||||
|
||||
# Shutdown kvstore
|
||||
if self.kvstore and hasattr(self.kvstore, "shutdown"):
|
||||
await self.kvstore.shutdown()
|
||||
|
||||
async def list_providers(self) -> ListProvidersResponse:
|
||||
run_config = self.config.run_config
|
||||
safe_config = StackRunConfig(**redact_sensitive_fields(run_config.model_dump()))
|
||||
providers_health = await self.get_providers_health()
|
||||
ret = []
|
||||
|
||||
# Add static providers (from run.yaml)
|
||||
for api, providers in safe_config.providers.items():
|
||||
for p in providers:
|
||||
# Skip providers that are not enabled
|
||||
|
|
@ -66,6 +139,32 @@ class ProviderImpl(Providers):
|
|||
)
|
||||
)
|
||||
|
||||
# Add dynamic providers (from kvstore)
|
||||
for _provider_id, conn_info in self.dynamic_providers.items():
|
||||
# Redact sensitive config for API response
|
||||
redacted_config = self._redact_sensitive_config(conn_info.config)
|
||||
|
||||
# Convert ProviderHealth to HealthResponse dict for API compatibility
|
||||
health_dict: HealthResponse | None = None
|
||||
if conn_info.health:
|
||||
health_dict = HealthResponse(
|
||||
status=conn_info.health.status,
|
||||
message=conn_info.health.message,
|
||||
)
|
||||
if conn_info.health.metrics:
|
||||
health_dict["metrics"] = conn_info.health.metrics
|
||||
|
||||
ret.append(
|
||||
ProviderInfo(
|
||||
api=conn_info.api,
|
||||
provider_id=conn_info.provider_id,
|
||||
provider_type=conn_info.provider_type,
|
||||
config=redacted_config,
|
||||
health=health_dict
|
||||
or HealthResponse(status=HealthStatus.NOT_IMPLEMENTED, message="No health check available"),
|
||||
)
|
||||
)
|
||||
|
||||
return ListProvidersResponse(data=ret)
|
||||
|
||||
async def inspect_provider(self, provider_id: str) -> ProviderInfo:
|
||||
|
|
@ -135,3 +234,378 @@ class ProviderImpl(Providers):
|
|||
providers_health[api_name] = health_response
|
||||
|
||||
return providers_health
|
||||
|
||||
# Storage helper methods for dynamic providers
|
||||
|
||||
async def _store_connection(self, info: ProviderConnectionInfo) -> None:
|
||||
"""Store provider connection info in kvstore.
|
||||
|
||||
:param info: ProviderConnectionInfo to store
|
||||
"""
|
||||
if not self.kvstore:
|
||||
raise RuntimeError("KVStore not initialized")
|
||||
|
||||
key = f"{PROVIDER_CONNECTIONS_PREFIX}{info.provider_id}"
|
||||
await self.kvstore.set(key, info.model_dump_json())
|
||||
logger.debug(f"Stored provider connection: {info.provider_id}")
|
||||
|
||||
async def _load_connection(self, provider_id: str) -> ProviderConnectionInfo | None:
|
||||
"""Load provider connection info from kvstore.
|
||||
|
||||
:param provider_id: Provider ID to load
|
||||
:returns: ProviderConnectionInfo if found, None otherwise
|
||||
"""
|
||||
if not self.kvstore:
|
||||
return None
|
||||
|
||||
key = f"{PROVIDER_CONNECTIONS_PREFIX}{provider_id}"
|
||||
value = await self.kvstore.get(key)
|
||||
if value:
|
||||
return ProviderConnectionInfo.model_validate_json(value)
|
||||
return None
|
||||
|
||||
async def _delete_connection(self, provider_id: str) -> None:
|
||||
"""Delete provider connection from kvstore.
|
||||
|
||||
:param provider_id: Provider ID to delete
|
||||
"""
|
||||
if not self.kvstore:
|
||||
raise RuntimeError("KVStore not initialized")
|
||||
|
||||
key = f"{PROVIDER_CONNECTIONS_PREFIX}{provider_id}"
|
||||
await self.kvstore.delete(key)
|
||||
logger.debug(f"Deleted provider connection: {provider_id}")
|
||||
|
||||
async def _list_connections(self) -> list[ProviderConnectionInfo]:
|
||||
"""List all dynamic provider connections from kvstore.
|
||||
|
||||
:returns: List of ProviderConnectionInfo
|
||||
"""
|
||||
if not self.kvstore:
|
||||
return []
|
||||
|
||||
start_key = PROVIDER_CONNECTIONS_PREFIX
|
||||
end_key = f"{PROVIDER_CONNECTIONS_PREFIX}\xff"
|
||||
values = await self.kvstore.values_in_range(start_key, end_key)
|
||||
return [ProviderConnectionInfo.model_validate_json(v) for v in values]
|
||||
|
||||
async def _load_dynamic_providers(self) -> None:
|
||||
"""Load dynamic providers from kvstore into runtime cache."""
|
||||
connections = await self._list_connections()
|
||||
for conn in connections:
|
||||
self.dynamic_providers[conn.provider_id] = conn
|
||||
logger.debug(f"Loaded dynamic provider: {conn.provider_id} (status: {conn.status})")
|
||||
|
||||
# Helper methods for dynamic provider management
|
||||
|
||||
def _redact_sensitive_config(self, config: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Redact sensitive fields in provider config for API responses.
|
||||
|
||||
:param config: Provider configuration dict
|
||||
:returns: Config with sensitive fields redacted
|
||||
"""
|
||||
return redact_sensitive_fields(config)
|
||||
|
||||
async def _instantiate_provider(self, conn_info: ProviderConnectionInfo) -> Any:
|
||||
"""Instantiate a provider from connection info.
|
||||
|
||||
Uses the resolver's instantiate_provider() to create a provider instance
|
||||
with all necessary dependencies.
|
||||
|
||||
:param conn_info: Provider connection information
|
||||
:returns: Instantiated provider implementation
|
||||
:raises RuntimeError: If provider cannot be instantiated
|
||||
"""
|
||||
if not self.provider_registry:
|
||||
raise RuntimeError("Provider registry not available for provider instantiation")
|
||||
if not self.dist_registry:
|
||||
raise RuntimeError("Distribution registry not available for provider instantiation")
|
||||
|
||||
# Get provider spec from registry
|
||||
api = Api(conn_info.api)
|
||||
if api not in self.provider_registry:
|
||||
raise ValueError(f"API {conn_info.api} not found in provider registry")
|
||||
|
||||
if conn_info.provider_type not in self.provider_registry[api]:
|
||||
raise ValueError(f"Provider type {conn_info.provider_type} not found for API {conn_info.api}")
|
||||
|
||||
provider_spec = self.provider_registry[api][conn_info.provider_type]
|
||||
|
||||
# Create ProviderWithSpec for instantiation
|
||||
provider_with_spec = ProviderWithSpec(
|
||||
provider_id=conn_info.provider_id,
|
||||
provider_type=conn_info.provider_type,
|
||||
config=conn_info.config,
|
||||
spec=provider_spec,
|
||||
)
|
||||
|
||||
# Resolve dependencies
|
||||
deps = {}
|
||||
for dep_api in provider_spec.api_dependencies:
|
||||
if dep_api not in self.deps:
|
||||
raise RuntimeError(
|
||||
f"Required dependency {dep_api.value} not available for provider {conn_info.provider_id}"
|
||||
)
|
||||
deps[dep_api] = self.deps[dep_api]
|
||||
|
||||
# Add optional dependencies if available
|
||||
for dep_api in provider_spec.optional_api_dependencies:
|
||||
if dep_api in self.deps:
|
||||
deps[dep_api] = self.deps[dep_api]
|
||||
|
||||
# Instantiate provider using resolver
|
||||
impl = await instantiate_provider(
|
||||
provider_with_spec,
|
||||
deps,
|
||||
{}, # inner_impls (empty for dynamic providers)
|
||||
self.dist_registry,
|
||||
self.config.run_config,
|
||||
self.policy,
|
||||
)
|
||||
|
||||
logger.debug(f"Instantiated provider {conn_info.provider_id} (type={conn_info.provider_type})")
|
||||
return impl
|
||||
|
||||
# Dynamic Provider Management Methods
|
||||
|
||||
async def register_provider(
|
||||
self,
|
||||
provider_id: str,
|
||||
api: str,
|
||||
provider_type: str,
|
||||
config: dict[str, Any],
|
||||
attributes: dict[str, list[str]] | None = None,
|
||||
) -> RegisterProviderResponse:
|
||||
"""Register a new provider.
|
||||
|
||||
This is used both for:
|
||||
- Providers from run.yaml (registered at startup)
|
||||
- Providers registered via API (registered at runtime)
|
||||
|
||||
All providers are stored in kvstore and treated equally.
|
||||
"""
|
||||
if not self.kvstore:
|
||||
raise RuntimeError("Dynamic provider management is not enabled (no kvstore configured)")
|
||||
|
||||
# Check if provider_id already exists
|
||||
if provider_id in self.dynamic_providers:
|
||||
raise ValueError(f"Provider {provider_id} already exists")
|
||||
|
||||
# Get authenticated user as owner
|
||||
user = get_authenticated_user()
|
||||
|
||||
# Create ProviderConnectionInfo
|
||||
now = datetime.now(UTC)
|
||||
conn_info = ProviderConnectionInfo(
|
||||
provider_id=provider_id,
|
||||
api=api,
|
||||
provider_type=provider_type,
|
||||
config=config,
|
||||
status=ProviderConnectionStatus.initializing,
|
||||
created_at=now,
|
||||
updated_at=now,
|
||||
owner=user,
|
||||
attributes=attributes,
|
||||
)
|
||||
|
||||
try:
|
||||
# Store in kvstore
|
||||
await self._store_connection(conn_info)
|
||||
|
||||
# Instantiate provider if we have a provider registry
|
||||
if self.provider_registry:
|
||||
impl = await self._instantiate_provider(conn_info)
|
||||
self.dynamic_provider_impls[provider_id] = impl
|
||||
|
||||
# Update status to connected after successful instantiation
|
||||
conn_info.status = ProviderConnectionStatus.connected
|
||||
conn_info.updated_at = datetime.now(UTC)
|
||||
|
||||
logger.info(
|
||||
f"Registered and instantiated dynamic provider {provider_id} (api={api}, type={provider_type})"
|
||||
)
|
||||
else:
|
||||
# No registry available - just mark as connected without instantiation
|
||||
# This can happen during testing or if provider management is disabled
|
||||
conn_info.status = ProviderConnectionStatus.connected
|
||||
conn_info.updated_at = datetime.now(UTC)
|
||||
logger.warning(f"Registered provider {provider_id} without instantiation (no registry)")
|
||||
|
||||
# Store updated status
|
||||
await self._store_connection(conn_info)
|
||||
|
||||
# Add to runtime cache
|
||||
self.dynamic_providers[provider_id] = conn_info
|
||||
|
||||
return RegisterProviderResponse(provider=conn_info)
|
||||
|
||||
except Exception as e:
|
||||
# Mark as failed and store
|
||||
conn_info.status = ProviderConnectionStatus.failed
|
||||
conn_info.error_message = str(e)
|
||||
conn_info.updated_at = datetime.now(UTC)
|
||||
await self._store_connection(conn_info)
|
||||
self.dynamic_providers[provider_id] = conn_info
|
||||
|
||||
logger.error(f"Failed to register provider {provider_id}: {e}")
|
||||
raise RuntimeError(f"Failed to register provider: {e}") from e
|
||||
|
||||
async def update_provider(
|
||||
self,
|
||||
provider_id: str,
|
||||
config: dict[str, Any] | None = None,
|
||||
attributes: dict[str, list[str]] | None = None,
|
||||
) -> UpdateProviderResponse:
|
||||
"""Update an existing provider's configuration.
|
||||
|
||||
Updates persist to kvstore and survive server restarts.
|
||||
This works for all providers (whether originally from run.yaml or API).
|
||||
"""
|
||||
if not self.kvstore:
|
||||
raise RuntimeError("Dynamic provider management is not enabled (no kvstore configured)")
|
||||
|
||||
# Check if provider exists
|
||||
if provider_id not in self.dynamic_providers:
|
||||
raise ValueError(f"Provider {provider_id} not found")
|
||||
|
||||
conn_info = self.dynamic_providers[provider_id]
|
||||
|
||||
# Update config if provided
|
||||
if config is not None:
|
||||
conn_info.config.update(config)
|
||||
|
||||
# Update attributes if provided
|
||||
if attributes is not None:
|
||||
conn_info.attributes = attributes
|
||||
|
||||
conn_info.updated_at = datetime.now(UTC)
|
||||
conn_info.status = ProviderConnectionStatus.initializing
|
||||
|
||||
try:
|
||||
# Store updated config
|
||||
await self._store_connection(conn_info)
|
||||
|
||||
# Hot-reload: Shutdown old instance and reinstantiate with new config
|
||||
if self.provider_registry:
|
||||
# Shutdown old instance if it exists
|
||||
if provider_id in self.dynamic_provider_impls:
|
||||
old_impl = self.dynamic_provider_impls[provider_id]
|
||||
if hasattr(old_impl, "shutdown"):
|
||||
try:
|
||||
await old_impl.shutdown()
|
||||
logger.debug(f"Shutdown old instance of provider {provider_id}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Error shutting down old instance of {provider_id}: {e}")
|
||||
|
||||
# Reinstantiate with new config
|
||||
impl = await self._instantiate_provider(conn_info)
|
||||
self.dynamic_provider_impls[provider_id] = impl
|
||||
|
||||
# Update status to connected after successful reinstantiation
|
||||
conn_info.status = ProviderConnectionStatus.connected
|
||||
conn_info.updated_at = datetime.now(UTC)
|
||||
await self._store_connection(conn_info)
|
||||
|
||||
logger.info(f"Hot-reloaded dynamic provider {provider_id}")
|
||||
else:
|
||||
# No registry - just update config without reinstantiation
|
||||
conn_info.status = ProviderConnectionStatus.connected
|
||||
conn_info.updated_at = datetime.now(UTC)
|
||||
await self._store_connection(conn_info)
|
||||
logger.warning(f"Updated provider {provider_id} config without hot-reload (no registry)")
|
||||
|
||||
return UpdateProviderResponse(provider=conn_info)
|
||||
|
||||
except Exception as e:
|
||||
conn_info.status = ProviderConnectionStatus.failed
|
||||
conn_info.error_message = str(e)
|
||||
conn_info.updated_at = datetime.now(UTC)
|
||||
await self._store_connection(conn_info)
|
||||
|
||||
logger.error(f"Failed to update provider {provider_id}: {e}")
|
||||
raise RuntimeError(f"Failed to update provider: {e}") from e
|
||||
|
||||
async def unregister_provider(self, provider_id: str) -> None:
|
||||
"""Unregister a provider.
|
||||
|
||||
Removes the provider from kvstore and shuts down its instance.
|
||||
This works for all providers (whether originally from run.yaml or API).
|
||||
"""
|
||||
if not self.kvstore:
|
||||
raise RuntimeError("Dynamic provider management is not enabled (no kvstore configured)")
|
||||
|
||||
# Check if provider exists
|
||||
if provider_id not in self.dynamic_providers:
|
||||
raise ValueError(f"Provider {provider_id} not found")
|
||||
|
||||
try:
|
||||
# Shutdown provider instance if it exists
|
||||
if provider_id in self.dynamic_provider_impls:
|
||||
impl = self.dynamic_provider_impls[provider_id]
|
||||
if hasattr(impl, "shutdown"):
|
||||
await impl.shutdown()
|
||||
del self.dynamic_provider_impls[provider_id]
|
||||
|
||||
# Remove from kvstore
|
||||
await self._delete_connection(provider_id)
|
||||
|
||||
# Remove from runtime cache
|
||||
del self.dynamic_providers[provider_id]
|
||||
|
||||
logger.info(f"Unregistered dynamic provider {provider_id}")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to unregister provider {provider_id}: {e}")
|
||||
raise RuntimeError(f"Failed to unregister provider: {e}") from e
|
||||
|
||||
async def test_provider_connection(self, provider_id: str) -> TestProviderConnectionResponse:
|
||||
"""Test a provider connection."""
|
||||
# Check if provider exists (static or dynamic)
|
||||
provider_impl = None
|
||||
|
||||
# Check dynamic providers first
|
||||
if provider_id in self.dynamic_provider_impls:
|
||||
provider_impl = self.dynamic_provider_impls[provider_id]
|
||||
# Check static providers
|
||||
elif provider_id in self.deps:
|
||||
provider_impl = self.deps[provider_id]
|
||||
|
||||
if not provider_impl:
|
||||
return TestProviderConnectionResponse(
|
||||
success=False, error_message=f"Provider {provider_id} not found or not initialized"
|
||||
)
|
||||
|
||||
# Check if provider has health method
|
||||
if not hasattr(provider_impl, "health"):
|
||||
return TestProviderConnectionResponse(
|
||||
success=False,
|
||||
health=HealthResponse(
|
||||
status=HealthStatus.NOT_IMPLEMENTED, message="Provider does not implement health check"
|
||||
),
|
||||
)
|
||||
|
||||
# Call health check
|
||||
try:
|
||||
health_result = await asyncio.wait_for(provider_impl.health(), timeout=5.0)
|
||||
|
||||
# Update health in dynamic provider cache if applicable
|
||||
if provider_id in self.dynamic_providers:
|
||||
conn_info = self.dynamic_providers[provider_id]
|
||||
conn_info.health = ProviderHealth.from_health_response(health_result)
|
||||
conn_info.last_health_check = datetime.now(UTC)
|
||||
await self._store_connection(conn_info)
|
||||
|
||||
logger.debug(f"Tested provider connection {provider_id}: status={health_result.get('status', 'UNKNOWN')}")
|
||||
|
||||
return TestProviderConnectionResponse(
|
||||
success=health_result.get("status") == HealthStatus.OK,
|
||||
health=health_result,
|
||||
)
|
||||
|
||||
except TimeoutError:
|
||||
health = HealthResponse(status=HealthStatus.ERROR, message="Health check timed out after 5 seconds")
|
||||
return TestProviderConnectionResponse(success=False, health=health)
|
||||
|
||||
except Exception as e:
|
||||
health = HealthResponse(status=HealthStatus.ERROR, message=f"Health check failed: {str(e)}")
|
||||
return TestProviderConnectionResponse(success=False, health=health, error_message=str(e))
|
||||
|
|
|
|||
|
|
@ -341,12 +341,21 @@ def cast_image_name_to_string(config_dict: dict[str, Any]) -> dict[str, Any]:
|
|||
return config_dict
|
||||
|
||||
|
||||
def add_internal_implementations(impls: dict[Api, Any], run_config: StackRunConfig) -> None:
|
||||
def add_internal_implementations(
|
||||
impls: dict[Api, Any],
|
||||
run_config: StackRunConfig,
|
||||
provider_registry=None,
|
||||
dist_registry=None,
|
||||
policy=None,
|
||||
) -> None:
|
||||
"""Add internal implementations (inspect and providers) to the implementations dictionary.
|
||||
|
||||
Args:
|
||||
impls: Dictionary of API implementations
|
||||
run_config: Stack run configuration
|
||||
provider_registry: Provider registry for dynamic provider instantiation
|
||||
dist_registry: Distribution registry
|
||||
policy: Access control policy
|
||||
"""
|
||||
inspect_impl = DistributionInspectImpl(
|
||||
DistributionInspectConfig(run_config=run_config),
|
||||
|
|
@ -355,7 +364,12 @@ def add_internal_implementations(impls: dict[Api, Any], run_config: StackRunConf
|
|||
impls[Api.inspect] = inspect_impl
|
||||
|
||||
providers_impl = ProviderImpl(
|
||||
ProviderImplConfig(run_config=run_config),
|
||||
ProviderImplConfig(
|
||||
run_config=run_config,
|
||||
provider_registry=provider_registry,
|
||||
dist_registry=dist_registry,
|
||||
policy=policy,
|
||||
),
|
||||
deps=impls,
|
||||
)
|
||||
impls[Api.providers] = providers_impl
|
||||
|
|
@ -416,13 +430,20 @@ class Stack:
|
|||
raise ValueError("storage.stores.metadata must be configured with a kv_* backend")
|
||||
dist_registry, _ = await create_dist_registry(stores.metadata, self.run_config.image_name)
|
||||
policy = self.run_config.server.auth.access_policy if self.run_config.server.auth else []
|
||||
provider_registry = self.provider_registry or get_provider_registry(self.run_config)
|
||||
|
||||
internal_impls = {}
|
||||
add_internal_implementations(internal_impls, self.run_config)
|
||||
add_internal_implementations(
|
||||
internal_impls,
|
||||
self.run_config,
|
||||
provider_registry=provider_registry,
|
||||
dist_registry=dist_registry,
|
||||
policy=policy,
|
||||
)
|
||||
|
||||
impls = await resolve_impls(
|
||||
self.run_config,
|
||||
self.provider_registry or get_provider_registry(self.run_config),
|
||||
provider_registry,
|
||||
dist_registry,
|
||||
policy,
|
||||
internal_impls,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue