Add Requesty as an OpenAI-compatible provider - #1979
Conversation
Greptile SummaryThis PR adds Requesty as a new OpenAI-compatible provider. The main changes are:
Confidence Score: 4/5The Requesty image path and endpoint registration need fixes before merging.
llm/transformer/requesty/outbound.go; internal/server/biz/channel_endpoint.go
|
| Filename | Overview |
|---|---|
| llm/transformer/requesty/outbound.go | Adds the main Requesty outbound transformer, with issues in image credential context, image request metadata, and error message handling. |
| llm/transformer/requesty/model.go | Adds Requesty response models for reasoning and image content conversion. |
| llm/transformer/requesty/aggregator.go | Adds stream chunk aggregation through the OpenAI aggregation path. |
| internal/server/biz/channel_endpoint.go | Adds Requesty default endpoints, but the registered formats do not match the implemented image capability cleanly. |
| internal/server/biz/channel_llm.go | Registers the Requesty outbound transformer for Requesty channels. |
| frontend/src/features/channels/data/config_channels.ts | Adds Requesty channel defaults, provider mapping, models, base URL, and icon wiring. |
| frontend/src/features/channels/data/config_providers.ts | Adds Requesty to frontend provider configuration. |
| frontend/src/features/channels/data/schema.ts | Adds requesty to the frontend channel type schema. |
| frontend/src/features/channels/components/requesty-icon.tsx | Adds a custom SVG icon for Requesty. |
Reviews (1): Last reviewed commit: "Add Requesty as an OpenAI-compatible pro..." | Re-trigger Greptile
| headers.Set("Accept", "application/json") | ||
|
|
||
| // Get API key from provider | ||
| apiKey := t.APIKeyProvider.Get(context.Background()) |
There was a problem hiding this comment.
Image Keys Lose Caller Context
When a Requesty image request uses a context-scoped key provider, this path fetches the API key with an empty background context instead of the caller context. Chat requests on the same channel can use the correct tenant or override key, while image requests can fail with 401s or use the wrong credential.
| rawReq := &httpclient.Request{ | ||
| Method: http.MethodPost, | ||
| URL: url, | ||
| Headers: headers, | ||
| Body: body, | ||
| Auth: auth, | ||
| ContentType: "application/json", | ||
| RequestType: llm.RequestTypeImage.String(), | ||
| } |
There was a problem hiding this comment.
Image Requests Have Empty Format
The custom image request sets RequestType but leaves APIFormat empty, even though outbound requests are expected to carry both fields for routing. Any composite router or response path that keys on APIFormat can treat Requesty image generation as an unknown or chat request, which can reject the call or parse the image response incorrectly.
| err := json.Unmarshal(rawErr.Body, &openaiError) | ||
| if err == nil { | ||
| return &llm.ResponseError{ | ||
| StatusCode: rawErr.StatusCode, | ||
| Detail: openaiError.ToLLMError(), | ||
| } | ||
| } |
There was a problem hiding this comment.
json.Unmarshal succeeds for any valid JSON object, including proxy or gateway error bodies that do not contain Requesty's error.message. This branch then returns an empty message with code 0, so callers and users lose the actual upstream failure text instead of getting the HTTP status fallback.
| channel.TypeRequesty: { | ||
| {APIFormat: llm.APIFormatOpenAIChatCompletion.String()}, | ||
| {APIFormat: llm.APIFormatOpenAISpeech.String()}, | ||
| {APIFormat: llm.APIFormatOpenAITranscription.String()}, | ||
| {APIFormat: llm.APIFormatOpenAITranslation.String()}, | ||
| }, |
There was a problem hiding this comment.
Image Capability Is Not Registered
The new transformer implements Requesty image generation through /chat/completions, and the README/frontend describe image generation support, but the default endpoint list only registers chat and audio formats. If endpoint registration gates routing or UI capability selection, Requesty image requests can be rejected as unsupported unless the user manually adds a custom endpoint.
| {APIFormat: llm.APIFormatOpenAISpeech.String()}, | ||
| {APIFormat: llm.APIFormatOpenAITranscription.String()}, | ||
| {APIFormat: llm.APIFormatOpenAITranslation.String()}, |
There was a problem hiding this comment.
Audio Formats Are Advertised By Default
These defaults enable speech, transcription, and translation for every Requesty channel, but the Requesty transformer only delegates those request types to the generic OpenAI transformer, which calls audio-specific paths under the Requesty base URL. If Requesty only supports the chat-completions router path described by this PR, users can select these default capabilities and receive runtime 404 or malformed-response failures.
| ep := gjson.GetBytes(event.Data, "error") | ||
| if ep.Exists() { | ||
| return nil, &llm.ResponseError{ | ||
| Detail: llm.ErrorDetail{ | ||
| Message: ep.String(), | ||
| }, | ||
| } |
There was a problem hiding this comment.
Stream Errors Return JSON Blobs
For streaming error chunks, this branch stores the whole error object as the user-facing message instead of extracting error.message. A normal error object such as a rate-limit response will be shown to callers as raw JSON, which can break message matching and produces poor diagnostics.
Adds Requesty as an OpenAI-compatible channel/provider, mirroring the existing OpenRouter integration.
Requesty (https://router.requesty.ai/v1) exposes an OpenAI-compatible chat-completions API, so this follows the same channel-transformer pattern as OpenRouter.
Changes:
llm/transformer/requesty/package mirroringllm/transformer/openrouter/(outbound/model/aggregator + tests). Base URL https://router.requesty.ai/v1, bearer auth,provider/modelnaming,reasoningreasoning field. Also mirrors the chat-completions image-generation path.requestyeverywhere OpenRouter is registered: the EntChanneltype enum (regenerated Ent/GraphQL code viamake generate), the biz default-endpoint + transformer switch, and the frontend channel/provider config, schema, and i18n (en + zh-CN).@lobehub/iconshas no Requesty entry), following the existing custom-icon pattern.live_integration_test.go) that routes a real request through the new transformer to the Requesty router; it is gated onREQUESTY_API_KEYand skips in CI.Verification:
cd llm && go build ./... && go test ./transformer/requesty/...— pass.go build ./...(root module) — pass.make generate— pass (regenerated Ent + GraphQL).openai/gpt-4o-mini— pass (real round-trip through the transformer).I work at Requesty. This mirrors the existing OpenRouter provider as closely as possible. Happy to adjust or close it if it's not a fit.