Skip to content

Add Requesty as an OpenAI-compatible provider - #1979

Open
Thibaultjaigu wants to merge 1 commit into
looplj:unstablefrom
Thibaultjaigu:add-requesty-provider
Open

Add Requesty as an OpenAI-compatible provider#1979
Thibaultjaigu wants to merge 1 commit into
looplj:unstablefrom
Thibaultjaigu:add-requesty-provider

Conversation

@Thibaultjaigu

Copy link
Copy Markdown

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:

  • New llm/transformer/requesty/ package mirroring llm/transformer/openrouter/ (outbound/model/aggregator + tests). Base URL https://router.requesty.ai/v1, bearer auth, provider/model naming, reasoning reasoning field. Also mirrors the chat-completions image-generation path.
  • Registered requesty everywhere OpenRouter is registered: the Ent Channel type enum (regenerated Ent/GraphQL code via make generate), the biz default-endpoint + transformer switch, and the frontend channel/provider config, schema, and i18n (en + zh-CN).
  • Added a Requesty icon component (custom SVG, since @lobehub/icons has no Requesty entry), following the existing custom-icon pattern.
  • Added a live integration test (live_integration_test.go) that routes a real request through the new transformer to the Requesty router; it is gated on REQUESTY_API_KEY and skips in CI.
  • README: added Requesty to the Supported Providers table.

Verification:

  • cd llm && go build ./... && go test ./transformer/requesty/... — pass.
  • go build ./... (root module) — pass.
  • make generate — pass (regenerated Ent + GraphQL).
  • Live test against https://router.requesty.ai/v1 with 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.

@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds Requesty as a new OpenAI-compatible provider. The main changes are:

  • New Requesty transformer package for chat, reasoning, streams, errors, and image responses.
  • Backend channel enum, endpoint, and transformer registration for requesty.
  • Frontend provider configuration, schema entries, icon, and locale labels.
  • Requesty tests and README provider table updates.

Confidence Score: 4/5

The Requesty image path and endpoint registration need fixes before merging.

  • Image requests can lose caller-scoped credentials.
  • Image requests are built without an API format used by routing code.
  • Requesty image support is implemented but not registered as a default capability.
  • Error handling can hide upstream failure messages.

llm/transformer/requesty/outbound.go; internal/server/biz/channel_endpoint.go

Security Review

Requesty image generation fetches API keys with an empty background context. Deployments that use context-scoped key providers can send image requests with the wrong tenant or override credential.

Important Files Changed

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())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 security 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.

Comment on lines +214 to +222
rawReq := &httpclient.Request{
Method: http.MethodPost,
URL: url,
Headers: headers,
Body: body,
Auth: auth,
ContentType: "application/json",
RequestType: llm.RequestTypeImage.String(),
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Comment on lines +511 to +517
err := json.Unmarshal(rawErr.Body, &openaiError)
if err == nil {
return &llm.ResponseError{
StatusCode: rawErr.StatusCode,
Detail: openaiError.ToLLMError(),
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Non-Requesty Errors Go Blank

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.

Comment on lines +162 to +167
channel.TypeRequesty: {
{APIFormat: llm.APIFormatOpenAIChatCompletion.String()},
{APIFormat: llm.APIFormatOpenAISpeech.String()},
{APIFormat: llm.APIFormatOpenAITranscription.String()},
{APIFormat: llm.APIFormatOpenAITranslation.String()},
},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 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.

Comment on lines +164 to +166
{APIFormat: llm.APIFormatOpenAISpeech.String()},
{APIFormat: llm.APIFormatOpenAITranscription.String()},
{APIFormat: llm.APIFormatOpenAITranslation.String()},

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Comment on lines +453 to +459
ep := gjson.GetBytes(event.Data, "error")
if ep.Exists() {
return nil, &llm.ResponseError{
Detail: llm.ErrorDetail{
Message: ep.String(),
},
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant