mirror of
https://github.com/mengxi-ream/read-frog.git
synced 2026-09-01 15:36:33 +08:00
fix(popup): allow Prompt selection with Built-in AI (#2094)
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@read-frog/extension": patch
|
||||
---
|
||||
|
||||
fix(popup): allow Prompt selection with Built-in AI
|
||||
@@ -8,13 +8,17 @@ import TranslatePromptSelector from "@/entrypoints/popup/components/translate-pr
|
||||
import { PromptSelector as TranslationHubPromptSelector } from "@/entrypoints/translation-hub/components/prompt-selector"
|
||||
import { DEFAULT_CONFIG } from "@/utils/constants/config"
|
||||
|
||||
const { providerAtom, selectedProvidersAtom, setTranslateMock, testState, translateAtom } =
|
||||
const { providerRefAtom, selectedProvidersAtom, setTranslateMock, testState, translateAtom } =
|
||||
vi.hoisted(() => ({
|
||||
providerAtom: {},
|
||||
providerRefAtom: {},
|
||||
selectedProvidersAtom: {},
|
||||
setTranslateMock: vi.fn<(value: Partial<Config["pageTranslation"]>) => Promise<void>>(),
|
||||
testState: {
|
||||
pageTranslation: null as Config["pageTranslation"] | null,
|
||||
pageTranslationProviderRef: null as
|
||||
| { kind: "local"; config: { provider: string } }
|
||||
| { kind: "system"; id: string; name: string; modelTier: "normal" | "advance" }
|
||||
| null,
|
||||
},
|
||||
translateAtom: {},
|
||||
}))
|
||||
@@ -25,7 +29,7 @@ vi.mock("jotai", () => ({
|
||||
return [testState.pageTranslation, setTranslateMock]
|
||||
},
|
||||
useAtomValue: (atom: object) => {
|
||||
if (atom === providerAtom) return { provider: "mock-llm" }
|
||||
if (atom === providerRefAtom) return testState.pageTranslationProviderRef
|
||||
if (atom === selectedProvidersAtom) return [{ provider: "mock-llm" }]
|
||||
throw new Error("Unexpected atom")
|
||||
},
|
||||
@@ -36,7 +40,7 @@ vi.mock("@/utils/atoms/config", () => ({
|
||||
}))
|
||||
|
||||
vi.mock("@/utils/atoms/provider", () => ({
|
||||
featureProviderConfigAtom: () => providerAtom,
|
||||
featureProviderRefAtom: () => providerRefAtom,
|
||||
}))
|
||||
|
||||
vi.mock("@/entrypoints/translation-hub/atoms", () => ({
|
||||
@@ -45,7 +49,7 @@ vi.mock("@/entrypoints/translation-hub/atoms", () => ({
|
||||
|
||||
vi.mock("@/types/config/provider", async (importOriginal) => ({
|
||||
...(await importOriginal<typeof import("@/types/config/provider")>()),
|
||||
isLLMProvider: () => true,
|
||||
isLLMProvider: (provider: string) => provider === "mock-llm",
|
||||
}))
|
||||
|
||||
vi.mock("@/components/help-tooltip", () => ({
|
||||
@@ -116,6 +120,10 @@ function createTranslateConfig(): Config["pageTranslation"] {
|
||||
describe("translation prompt selectors", () => {
|
||||
beforeEach(() => {
|
||||
testState.pageTranslation = createTranslateConfig()
|
||||
testState.pageTranslationProviderRef = {
|
||||
kind: "local",
|
||||
config: { provider: "mock-llm" },
|
||||
}
|
||||
setTranslateMock.mockReset()
|
||||
setTranslateMock.mockResolvedValue()
|
||||
})
|
||||
@@ -138,6 +146,35 @@ describe("translation prompt selectors", () => {
|
||||
})
|
||||
})
|
||||
|
||||
it("keeps prompt selection available for Built-in AI", () => {
|
||||
testState.pageTranslationProviderRef = {
|
||||
kind: "system",
|
||||
id: "read-frog-free-ai",
|
||||
name: "Built-in AI",
|
||||
modelTier: "normal",
|
||||
}
|
||||
render(<TranslatePromptSelector />)
|
||||
|
||||
fireEvent.click(screen.getByRole("option", { name: "Custom" }))
|
||||
expect(setTranslateMock).toHaveBeenCalledWith({
|
||||
customPromptsConfig: {
|
||||
...testState.pageTranslation!.customPromptsConfig,
|
||||
promptId: "custom",
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it("keeps prompt selection hidden for a local translation-only provider", () => {
|
||||
testState.pageTranslationProviderRef = {
|
||||
kind: "local",
|
||||
config: { provider: "google-translate" },
|
||||
}
|
||||
|
||||
render(<TranslatePromptSelector />)
|
||||
|
||||
expect(screen.queryByRole("combobox")).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("shows the selected built-in and uses the same order in Translation Hub", () => {
|
||||
testState.pageTranslation!.customPromptsConfig.promptId = "precision-rewrite"
|
||||
render(<TranslationHubPromptSelector />)
|
||||
|
||||
@@ -11,15 +11,18 @@ import {
|
||||
} from "@/components/ui/base-ui/select"
|
||||
import { isLLMProvider } from "@/types/config/provider"
|
||||
import { configFieldsAtomMap } from "@/utils/atoms/config"
|
||||
import { featureProviderConfigAtom } from "@/utils/atoms/provider"
|
||||
import { featureProviderRefAtom } from "@/utils/atoms/provider"
|
||||
import { DEFAULT_TRANSLATE_PROMPT_ID } from "@/utils/constants/prompt"
|
||||
import { i18n } from "@/utils/i18n"
|
||||
|
||||
export default function TranslatePromptSelector() {
|
||||
const translateProviderConfig = useAtomValue(featureProviderConfigAtom("pageTranslation"))
|
||||
const translateProviderRef = useAtomValue(featureProviderRefAtom("pageTranslation"))
|
||||
const [translateConfig, setTranslateConfig] = useAtom(configFieldsAtomMap.pageTranslation)
|
||||
|
||||
if (!translateProviderConfig?.provider || !isLLMProvider(translateProviderConfig?.provider))
|
||||
if (
|
||||
!translateProviderRef ||
|
||||
(translateProviderRef.kind === "local" && !isLLMProvider(translateProviderRef.config.provider))
|
||||
)
|
||||
return null
|
||||
|
||||
const customPromptsConfig = translateConfig.customPromptsConfig
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import type { PartialDeep } from "type-fest"
|
||||
import type { ProviderConfig } from "@/types/config/provider"
|
||||
import { createStore } from "jotai"
|
||||
import { describe, expect, it } from "vitest"
|
||||
import { configAtom } from "@/utils/atoms/config"
|
||||
import { DEFAULT_CONFIG } from "@/utils/constants/config"
|
||||
import { BUILT_IN_AI_PROVIDER_ID } from "@/utils/constants/provider-ids"
|
||||
import { DEFAULT_PROVIDER_CONFIG } from "@/utils/constants/providers"
|
||||
import { updateLLMProviderConfig, updateProviderConfig } from "../provider"
|
||||
import { featureProviderRefAtom, updateLLMProviderConfig, updateProviderConfig } from "../provider"
|
||||
|
||||
type OpenAIProviderConfig = Extract<ProviderConfig, { provider: "openai" }>
|
||||
type BedrockProviderConfig = Extract<ProviderConfig, { provider: "bedrock" }>
|
||||
@@ -70,3 +74,27 @@ describe("provider config updates", () => {
|
||||
).toThrow(/Invalid/)
|
||||
})
|
||||
})
|
||||
|
||||
describe("feature provider refs", () => {
|
||||
it("resolves Built-in AI without requiring a persisted provider config row", () => {
|
||||
const store = createStore()
|
||||
const config = structuredClone(DEFAULT_CONFIG)
|
||||
config.pageTranslation.providerId = BUILT_IN_AI_PROVIDER_ID
|
||||
store.set(configAtom, config)
|
||||
|
||||
expect(store.get(featureProviderRefAtom("pageTranslation"))).toMatchObject({
|
||||
kind: "system",
|
||||
id: BUILT_IN_AI_PROVIDER_ID,
|
||||
modelTier: "normal",
|
||||
})
|
||||
})
|
||||
|
||||
it("continues to resolve persisted providers as local refs", () => {
|
||||
const store = createStore()
|
||||
|
||||
expect(store.get(featureProviderRefAtom("pageTranslation"))).toMatchObject({
|
||||
kind: "local",
|
||||
id: DEFAULT_CONFIG.pageTranslation.providerId,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -5,16 +5,17 @@ import { deepmerge } from "deepmerge-ts"
|
||||
import { atom } from "jotai"
|
||||
import { atomFamily } from "jotai-family"
|
||||
import { llmProviderConfigItemSchema, providerConfigItemSchema } from "@/types/config/provider"
|
||||
import { resolveProviderRefForCapability } from "@/utils/providers/provider-registry"
|
||||
import { getProviderConfigById } from "../config/helpers"
|
||||
import { FEATURE_PROVIDER_DEFS } from "../constants/feature-providers"
|
||||
import { configAtom, configFieldsAtomMap } from "./config"
|
||||
|
||||
export const featureProviderConfigAtom = atomFamily((featureKey: FeatureKey) =>
|
||||
export const featureProviderRefAtom = atomFamily((featureKey: FeatureKey) =>
|
||||
atom((get) => {
|
||||
const config = get(configAtom)
|
||||
const def = FEATURE_PROVIDER_DEFS[featureKey]
|
||||
const providerId = def.getProviderId(config)
|
||||
return getProviderConfigById(config.providersConfig, providerId) ?? null
|
||||
return resolveProviderRefForCapability(featureKey, config.providersConfig, providerId)
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user