feat: Anthropic provider/inbound + embeddings
- Anthropic outbound provider: /v1/messages, system extraction, x-api-key + anthropic-version; streaming translates Anthropic SSE -> unified OpenAI wire format - Anthropic inbound adapter (POST /v1/messages): non-streaming JSON + streaming SSE (OpenAI -> Anthropic event translation) - Embeddings: POST /v1/embeddings inbound + Provider::embed(); embedding models resolvable from the pool (registry no longer skips them) - protocols mounted always, gated on live flags; admin meta marks them implemented
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
//! Inbound OpenAI Embeddings adapter: `POST /v1/embeddings`.
|
||||
//! Resolves an embedding model (explicit id, or the first `kind = "embedding"`),
|
||||
//! calls the provider, and forwards the OpenAI-style response body.
|
||||
|
||||
use crate::error::{GatewayError, Result};
|
||||
use crate::state::SharedState;
|
||||
use axum::{extract::State, response::IntoResponse, routing::post, Json, Router};
|
||||
use serde::Deserialize;
|
||||
|
||||
pub fn routes() -> Router<SharedState> {
|
||||
Router::new().route("/v1/embeddings", post(handler))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct EmbeddingsRequest {
|
||||
#[serde(default)]
|
||||
model: String,
|
||||
input: serde_json::Value,
|
||||
}
|
||||
|
||||
async fn handler(
|
||||
State(state): State<SharedState>,
|
||||
Json(req): Json<EmbeddingsRequest>,
|
||||
) -> Result<impl IntoResponse> {
|
||||
let live = state.live();
|
||||
if !live.cfg.protocols.openai_embeddings {
|
||||
return Err(GatewayError::InvalidRequest(
|
||||
"openai_embeddings protocol is disabled".into(),
|
||||
));
|
||||
}
|
||||
|
||||
// resolve the embedding model: explicit real id, else first kind == "embedding"
|
||||
let model_id = if !req.model.is_empty()
|
||||
&& req.model != "cortiq-auto"
|
||||
&& live.registry.get(&req.model).is_some()
|
||||
{
|
||||
req.model.clone()
|
||||
} else {
|
||||
live.cfg
|
||||
.models
|
||||
.iter()
|
||||
.find(|m| m.kind == "embedding")
|
||||
.map(|m| m.id.clone())
|
||||
.ok_or_else(|| GatewayError::InvalidRequest("no embedding model configured".into()))?
|
||||
};
|
||||
|
||||
let provider = live.registry.get(&model_id).ok_or_else(|| {
|
||||
GatewayError::UpstreamUnavailable(format!("embedding model '{model_id}' is not available"))
|
||||
})?;
|
||||
let body = provider.embed(req.input).await?;
|
||||
Ok(Json(body))
|
||||
}
|
||||
Reference in New Issue
Block a user