diff --git a/plugins/festinger/festinger/main.py b/plugins/festinger/festinger/main.py index 1362650..14c833d 100644 --- a/plugins/festinger/festinger/main.py +++ b/plugins/festinger/festinger/main.py @@ -1103,6 +1103,14 @@ async def openai_chat_completions(request: Request) -> Response: raise +# Alias: some LiteLLM provider types (custom_openai, openai_like) omit the +# /v1 prefix and post directly to /chat/completions. +@app.post("/chat/completions") +async def openai_chat_completions_no_prefix(request: Request) -> Response: + """Alias for /v1/chat/completions — handles LiteLLM providers that omit /v1.""" + return await openai_chat_completions(request) + + # --------------------------------------------------------------------------- # /iknowthat — manual write path # --------------------------------------------------------------------------- @@ -3070,6 +3078,14 @@ async def models_ui() -> str: @app.api_route("/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "HEAD"]) async def passthrough(path: str, request: Request) -> Response: cfg = request.app.state.yaml_config + + # Some LiteLLM configurations build wrong paths like v1/messages/chat/completions + # (when api_base already includes /v1/messages) or v1/chat/completions without + # a leading slash. Redirect all chat-completion variants to the proper handler. + if path.endswith("chat/completions") and request.method == "POST": + log.info("passthrough redirect %s → /v1/chat/completions", path) + return await openai_chat_completions(request) + if path.startswith("v1/"): upstream = cfg["upstream_anthropic"] relay_headers = ANTHROPIC_RELAY_HEADERS