Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ export const SECRET_STATE_KEYS = [
"ioIntelligenceApiKey",
"vercelAiGatewayApiKey",
"basetenApiKey",
"poeApiKey",
] as const

// Global secrets that are part of GlobalSettings (not ProviderSettings)
Expand Down
10 changes: 10 additions & 0 deletions packages/types/src/provider-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
mistralModels,
moonshotModels,
openAiNativeModels,
poeModels,
qwenCodeModels,
sambaNovaModels,
vertexModels,
Expand Down Expand Up @@ -135,6 +136,7 @@ export const providerNames = [
"moonshot",
"minimax",
"openai-native",
"poe",
"qwen-code",
"roo",
"sambanova",
Expand Down Expand Up @@ -430,6 +432,10 @@ const basetenSchema = apiModelIdProviderModelSchema.extend({
basetenApiKey: z.string().optional(),
})

const poeSchema = apiModelIdProviderModelSchema.extend({
poeApiKey: z.string().optional(),
})

const defaultSchema = z.object({
apiProvider: z.undefined(),
})
Expand All @@ -443,6 +449,7 @@ export const providerSettingsSchemaDiscriminated = z.discriminatedUnion("apiProv
vertexSchema.merge(z.object({ apiProvider: z.literal("vertex") })),
openAiSchema.merge(z.object({ apiProvider: z.literal("openai") })),
ollamaSchema.merge(z.object({ apiProvider: z.literal("ollama") })),
poeSchema.merge(z.object({ apiProvider: z.literal("poe") })),
vsCodeLmSchema.merge(z.object({ apiProvider: z.literal("vscode-lm") })),
lmStudioSchema.merge(z.object({ apiProvider: z.literal("lmstudio") })),
geminiSchema.merge(z.object({ apiProvider: z.literal("gemini") })),
Expand Down Expand Up @@ -486,6 +493,7 @@ export const providerSettingsSchema = z.object({
...vertexSchema.shape,
...openAiSchema.shape,
...ollamaSchema.shape,
...poeSchema.shape,
...vsCodeLmSchema.shape,
...lmStudioSchema.shape,
...geminiSchema.shape,
Expand Down Expand Up @@ -577,6 +585,7 @@ export const modelIdKeysByProvider: Record<TypicalProvider, ModelIdKey> = {
vertex: "apiModelId",
"openai-native": "openAiModelId",
ollama: "ollamaModelId",
poe: "apiModelId",
lmstudio: "lmStudioModelId",
gemini: "apiModelId",
"gemini-cli": "apiModelId",
Expand Down Expand Up @@ -725,6 +734,7 @@ export const MODELS_BY_PROVIDER: Record<
xai: { id: "xai", label: "xAI (Grok)", models: Object.keys(xaiModels) },
zai: { id: "zai", label: "Z.ai", models: Object.keys(internationalZAiModels) },
baseten: { id: "baseten", label: "Baseten", models: Object.keys(basetenModels) },
poe: { id: "poe", label: "Poe", models: Object.keys(poeModels) },

// Dynamic providers; models pulled from remote APIs.
glama: { id: "glama", label: "Glama", models: [] },
Expand Down
4 changes: 4 additions & 0 deletions packages/types/src/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export * from "./moonshot.js"
export * from "./ollama.js"
export * from "./openai.js"
export * from "./openrouter.js"
export * from "./poe.js"
export * from "./qwen-code.js"
export * from "./requesty.js"
export * from "./roo.js"
Expand Down Expand Up @@ -51,6 +52,7 @@ import { litellmDefaultModelId } from "./lite-llm.js"
import { mistralDefaultModelId } from "./mistral.js"
import { moonshotDefaultModelId } from "./moonshot.js"
import { openRouterDefaultModelId } from "./openrouter.js"
import { poeDefaultModelId } from "./poe.js"
import { qwenCodeDefaultModelId } from "./qwen-code.js"
import { requestyDefaultModelId } from "./requesty.js"
import { rooDefaultModelId } from "./roo.js"
Expand Down Expand Up @@ -111,6 +113,8 @@ export function getProviderDefaultModelId(
return moonshotDefaultModelId
case "minimax":
return minimaxDefaultModelId
case "poe":
return poeDefaultModelId
case "zai":
return options?.isChina ? mainlandZAiDefaultModelId : internationalZAiDefaultModelId
case "openai-native":
Expand Down
90 changes: 90 additions & 0 deletions packages/types/src/providers/poe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import type { ModelInfo } from "../model.js"

// Poe
// https://creator.poe.com/docs/external-applications/openai-compatible-api

export const poeModels = {
"gpt-5.1-codex-mini": {
maxTokens: 128_000,
contextWindow: 400_000,
supportsNativeTools: true,
supportsImages: true,
supportsPromptCache: true,
supportsTemperature: false,
},
"gpt-5.1-codex": {
maxTokens: 128_000,
contextWindow: 400_000,
supportsNativeTools: true,
supportsImages: true,
supportsPromptCache: true,
supportsTemperature: false,
},
"gpt-5-mini": {
maxTokens: 128_000,
contextWindow: 400_000,
supportsNativeTools: true,
supportsImages: true,
supportsPromptCache: true,
supportsVerbosity: true,
supportsTemperature: false,
},
"gpt-5.1": {
maxTokens: 128_000,
contextWindow: 400_000,
supportsNativeTools: true,
supportsImages: true,
supportsPromptCache: true,
supportsVerbosity: true,
supportsTemperature: false,
},
"claude-sonnet-4.5": {
maxTokens: 64_000,
contextWindow: 200_000,
supportsImages: true,
supportsPromptCache: true,
},
"claude-haiku-4.5": {
maxTokens: 64_000,
contextWindow: 200_000,
supportsImages: false,
supportsPromptCache: true,
},
"claude-opus-4.1": {
maxTokens: 32_000,
contextWindow: 200_000,
supportsImages: true,
supportsPromptCache: true,
},
"gemini-3-pro": {
maxTokens: 65_536,
contextWindow: 1_048_576,
supportsImages: true,
supportsNativeTools: true,
supportsPromptCache: true,
supportsTemperature: true,
defaultTemperature: 1,
},
"grok-code-fast-1": {
maxTokens: 16_384,
contextWindow: 262_144,
supportsImages: false,
supportsPromptCache: true,
},
"grok-4": {
maxTokens: 8_192,
contextWindow: 256_000,
supportsImages: true,
supportsPromptCache: true,
},
"deepseek-r1": {
maxTokens: 65_536,
contextWindow: 128_000,
supportsImages: false,
supportsPromptCache: true,
},
} as const satisfies Record<string, ModelInfo>

export type PoeModelId = keyof typeof poeModels

export const poeDefaultModelId = "gpt-5.1-codex-mini" satisfies PoeModelId
3 changes: 3 additions & 0 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import {
DeepInfraHandler,
MiniMaxHandler,
BasetenHandler,
PoeHandler,
} from "./providers"
import { NativeOllamaHandler } from "./providers/native-ollama"

Expand Down Expand Up @@ -200,6 +201,8 @@ export function buildApiHandler(configuration: ProviderSettings): ApiHandler {
return new MiniMaxHandler(options)
case "baseten":
return new BasetenHandler(options)
case "poe":
return new PoeHandler(options)
default:
apiProvider satisfies "gemini-cli" | undefined
return new AnthropicHandler(options)
Expand Down
1 change: 1 addition & 0 deletions src/api/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,4 @@ export { VercelAiGatewayHandler } from "./vercel-ai-gateway"
export { DeepInfraHandler } from "./deepinfra"
export { MiniMaxHandler } from "./minimax"
export { BasetenHandler } from "./baseten"
export { PoeHandler } from "./poe"
18 changes: 18 additions & 0 deletions src/api/providers/poe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { type PoeModelId, poeDefaultModelId, poeModels } from "@roo-code/types"

import type { ApiHandlerOptions } from "../../shared/api"
import { BaseOpenAiCompatibleProvider } from "./base-openai-compatible-provider"

export class PoeHandler extends BaseOpenAiCompatibleProvider<PoeModelId> {
constructor(options: ApiHandlerOptions) {
super({
...options,
providerName: "Poe",
baseURL: "https://api.poe.com/v1",
apiKey: options.poeApiKey,
defaultProviderModelId: poeDefaultModelId,
providerModels: poeModels,
defaultTemperature: 1.0,
})
}
}
7 changes: 7 additions & 0 deletions webview-ui/src/components/settings/ApiOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
chutesDefaultModelId,
basetenDefaultModelId,
bedrockDefaultModelId,
poeDefaultModelId,
vertexDefaultModelId,
sambaNovaDefaultModelId,
internationalZAiDefaultModelId,
Expand Down Expand Up @@ -88,6 +89,7 @@ import {
OpenAI,
OpenAICompatible,
OpenRouter,
Poe,
QwenCode,
Requesty,
Roo,
Expand Down Expand Up @@ -361,6 +363,7 @@ const ApiOptions = ({
chutes: { field: "apiModelId", default: chutesDefaultModelId },
baseten: { field: "apiModelId", default: basetenDefaultModelId },
bedrock: { field: "apiModelId", default: bedrockDefaultModelId },
poe: { field: "apiModelId", default: poeDefaultModelId },
vertex: { field: "apiModelId", default: vertexDefaultModelId },
sambanova: { field: "apiModelId", default: sambaNovaDefaultModelId },
zai: {
Expand Down Expand Up @@ -619,6 +622,10 @@ const ApiOptions = ({
/>
)}

{selectedProvider === "poe" && (
<Poe apiConfiguration={apiConfiguration} setApiConfigurationField={setApiConfigurationField} />
)}

{selectedProvider === "vertex" && (
<Vertex
apiConfiguration={apiConfiguration}
Expand Down
3 changes: 3 additions & 0 deletions webview-ui/src/components/settings/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
featherlessModels,
minimaxModels,
basetenModels,
poeModels,
} from "@roo-code/types"

export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, ModelInfo>>> = {
Expand All @@ -44,6 +45,7 @@ export const MODELS_BY_PROVIDER: Partial<Record<ProviderName, Record<string, Mod
featherless: featherlessModels,
minimax: minimaxModels,
baseten: basetenModels,
poe: poeModels,
}

export const PROVIDERS = [
Expand Down Expand Up @@ -83,4 +85,5 @@ export const PROVIDERS = [
{ value: "vercel-ai-gateway", label: "Vercel AI Gateway" },
{ value: "minimax", label: "MiniMax" },
{ value: "baseten", label: "Baseten" },
{ value: "poe", label: "Poe" },
].sort((a, b) => a.label.localeCompare(b.label))
50 changes: 50 additions & 0 deletions webview-ui/src/components/settings/providers/Poe.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useCallback } from "react"
import { VSCodeTextField } from "@vscode/webview-ui-toolkit/react"

import type { ProviderSettings } from "@roo-code/types"

import { useAppTranslation } from "@src/i18n/TranslationContext"
import { VSCodeButtonLink } from "@src/components/common/VSCodeButtonLink"

import { inputEventTransform } from "../transforms"

type PoeProps = {
apiConfiguration: ProviderSettings
setApiConfigurationField: (field: keyof ProviderSettings, value: ProviderSettings[keyof ProviderSettings]) => void
}

export const Poe = ({ apiConfiguration, setApiConfigurationField }: PoeProps) => {
const { t } = useAppTranslation()

const handleInputChange = useCallback(
<K extends keyof ProviderSettings, E>(
field: K,
transform: (event: E) => ProviderSettings[K] = inputEventTransform,
) =>
(event: E | Event) => {
setApiConfigurationField(field, transform(event as E))
},
[setApiConfigurationField],
)

return (
<>
<VSCodeTextField
value={apiConfiguration?.poeApiKey || ""}
type="password"
onInput={handleInputChange("poeApiKey")}
placeholder={t("settings:placeholders.apiKey")}
className="w-full">
<label className="block font-medium mb-1">{t("settings:providers.poeApiKey")}</label>
</VSCodeTextField>
<div className="text-sm text-vscode-descriptionForeground -mt-2">
{t("settings:providers.apiKeyStorageNotice")}
</div>
{!apiConfiguration?.poeApiKey && (
<VSCodeButtonLink href="https://poe.com/api_key" appearance="secondary">
{t("settings:providers.getPoeApiKey")}
</VSCodeButtonLink>
)}
</>
)
}
1 change: 1 addition & 0 deletions webview-ui/src/components/settings/providers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ export { VercelAiGateway } from "./VercelAiGateway"
export { DeepInfra } from "./DeepInfra"
export { MiniMax } from "./MiniMax"
export { Baseten } from "./Baseten"
export { Poe } from "./Poe"
6 changes: 6 additions & 0 deletions webview-ui/src/components/ui/hooks/useSelectedModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
featherlessModels,
ioIntelligenceModels,
basetenModels,
poeModels,
qwenCodeModels,
BEDROCK_1M_CONTEXT_MODEL_IDS,
isDynamicProvider,
Expand Down Expand Up @@ -226,6 +227,11 @@ function getSelectedModel({

return { id, info: baseInfo }
}
case "poe": {
const id = apiConfiguration.apiModelId ?? defaultModelId
const info = poeModels[id as keyof typeof poeModels]
return { id, info }
}
case "vertex": {
const id = apiConfiguration.apiModelId ?? defaultModelId
const info = vertexModels[id as keyof typeof vertexModels]
Expand Down
2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/ca/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/de/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/en/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,8 @@
"minimaxApiKey": "MiniMax API Key",
"getMiniMaxApiKey": "Get MiniMax API Key",
"minimaxBaseUrl": "MiniMax Entrypoint",
"poeApiKey": "Poe API Key",
"getPoeApiKey": "Get Poe API Key",
"zaiApiKey": "Z AI API Key",
"getZaiApiKey": "Get Z AI API Key",
"zaiEntrypoint": "Z AI Entrypoint",
Expand Down
2 changes: 2 additions & 0 deletions webview-ui/src/i18n/locales/es/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading