-
Notifications
You must be signed in to change notification settings - Fork 207
feature: Add FastAPI support with conditional endpoint routing #216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Add support for Kokoro-FastAPI alongside the standard Kokoro implementation. Users can now toggle between standard and FastAPI endpoints in settings. Changes: - Add kokoro_api_type config key (default: 'standard') - Implement conditional API routing in kokoro.ts - Handle different voice list response formats for each API - Add API type selector to KokoroSettingsPage with help text - Integrate kokoroApiType state management in main settings The implementation allows seamless switching between: - Standard Kokoro: POST /tts endpoint - Kokoro-FastAPI: POST /v1/audio/speech (OpenAI-compatible)
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
@smirgol is attempting to deploy a commit to the heyamica Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughAdds a Kokoro API type option to config and UI, threads a typed Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant SettingsUI as Settings Page
participant KokoroModule as kokoro.ts
participant KokoroAPI as External Kokoro API
User->>SettingsUI: select Kokoro API Type
SettingsUI->>SettingsUI: setKokoroApiType(value)
SettingsUI->>SettingsUI: updateConfig("kokoro_api_type", value)
SettingsUI->>KokoroModule: kokoroVoiceList()
KokoroModule->>KokoroModule: choose apiConfigs[kokoroApiType]
KokoroModule->>KokoroAPI: HTTP request (voices endpoint per config)
KokoroAPI-->>KokoroModule: voices response
KokoroModule->>SettingsUI: normalized voice list
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20–30 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/components/settings.tsx(2 hunks)src/components/settings/KokoroSettingsPage.tsx(2 hunks)src/features/kokoro/kokoro.ts(1 hunks)src/utils/config.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/components/settings/KokoroSettingsPage.tsx (3)
src/components/settings/common.tsx (1)
FormRow(74-88)src/i18n/index.ts (1)
t(26-26)src/utils/config.ts (1)
updateConfig(157-172)
src/components/settings.tsx (1)
src/utils/config.ts (1)
config(140-155)
src/features/kokoro/kokoro.ts (1)
src/utils/config.ts (1)
config(140-155)
🔇 Additional comments (1)
src/utils/config.ts (1)
80-83: Newkokoro_api_typedefault is consistent and safeThe new default key cleanly follows the existing config pattern and matches downstream usage in
settings.tsxandkokoro.ts. No issues from a config perspective.
Add error checking for failed API responses in fetchVoiceList(). Add defensive fallback for unexpected kokoro_api_type config values. Fix type safety and voice list reactivity in KokoroSettingsPage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/features/kokoro/kokoro.ts (1)
3-77: Kokoro API abstraction looks solid; consider tightening voice list typing (optional)The
ApiType+apiConfigssetup with a defensive default forkokoro_api_typeand consistentresponse.okchecks in bothfetchAudioandfetchVoiceListlooks good and should make adding more variants straightforward.If you want to push type-safety a bit further, you could narrow
parseVoiceListaway fromanyand model the FastAPI JSON shape explicitly (e.g.interface FastApiVoices { data: { id: string }[] }) so the parser returns a guaranteed{ voices: string[] }rather than falling back toreturn data. That would line up with how you’re already using a discriminatedApiTypeunion. Based on learnings, this matches your preference for stronger typing on external API boundaries.src/components/settings/KokoroSettingsPage.tsx (1)
59-67: Narrow the select’s change event to preserve theApiTypeunion (optional)Runtime behavior is fine, but
React.ChangeEvent<any>plus an untypedvalueweakens the benefit of theApiTypeunion on the setter. You can keep things type-safe with:- <select + <select className="mt-2 block w-full rounded-md border-0 py-1.5 pl-3 pr-10 text-gray-900 ring-1 ring-inset ring-gray-300 focus:ring-2 focus:ring-indigo-600 sm:text-sm sm:leading-6" value={kokoroApiType} - onChange={(event: React.ChangeEvent<any>) => { - const value = event.target.value; - setKokoroApiType(value); + onChange={(event: React.ChangeEvent<HTMLSelectElement>) => { + const value = event.target.value as ApiType; + setKokoroApiType(value); updateConfig("kokoro_api_type", value); setSettingsUpdated(true); }}This keeps
kokoroApiTypestrictly aligned with the union while still matching the actual option values. Based on learnings, this leans into the stronger typing you prefer.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/components/settings.tsx(4 hunks)src/components/settings/KokoroSettingsPage.tsx(3 hunks)src/features/kokoro/kokoro.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-13T07:01:57.942Z
Learnt from: AlphaEcho11
Repo: semperai/amica PR: 213
File: src/features/chat/koboldAiChat.ts:0-0
Timestamp: 2025-08-13T07:01:57.942Z
Learning: AlphaEcho11 prefers strong typing and comprehensive error handling for Tauri invoke calls in frontend TypeScript code. They implemented an OpenAIResponse interface and wrapped invoke calls in try/catch blocks in openAiChat.ts, and expect similar patterns to be applied consistently across similar proxy call implementations.
Applied to files:
src/features/kokoro/kokoro.ts
🧬 Code graph analysis (3)
src/features/kokoro/kokoro.ts (2)
src/features/chat/chat.ts (1)
fetchAudio(638-707)src/utils/config.ts (1)
config(140-155)
src/components/settings/KokoroSettingsPage.tsx (4)
src/features/kokoro/kokoro.ts (1)
ApiType(3-3)src/components/settings/common.tsx (1)
FormRow(74-88)src/i18n/index.ts (1)
t(26-26)src/utils/config.ts (1)
updateConfig(157-172)
src/components/settings.tsx (1)
src/utils/config.ts (1)
config(140-155)
🔇 Additional comments (1)
src/components/settings.tsx (1)
265-311: Notification effect dependencies now correctly include Kokoro settingsIncluding
kokoroUrl,kokoroVoice,kokoroApiType, andsettingsUpdatedin the dependencies (and fixing the double comma) ensures the “Successfully saved!” toast also fires when only Kokoro settings—especially the API type—change. This resolves the previous edge case without altering the effect’s semantics.
| const [kokoroUrl, setKokoroUrl] = useState(config("kokoro_url")); | ||
| const [kokoroApiType, setKokoroApiType] = useState(config("kokoro_api_type")); | ||
| const [kokoroVoice, setKokoroVoice] = useState(config("kokoro_voice")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type kokoroApiType state as ApiType and add a defensive default
Right now kokoroApiType is inferred as string:
const [kokoroApiType, setKokoroApiType] = useState(config("kokoro_api_type"));but KokoroSettingsPage expects kokoroApiType: ApiType and setKokoroApiType: (key: ApiType) => void. This both undermines the union’s safety and is likely to trip the type checker.
You can align state with the ApiType union and mirror the defensive fallback you already use in kokoro.ts:
-import { KokoroSettingsPage } from "./settings/KokoroSettingsPage";
+import { KokoroSettingsPage } from "./settings/KokoroSettingsPage";
+import type { ApiType } from "@/features/kokoro/kokoro";
@@
- const [kokoroUrl, setKokoroUrl] = useState(config("kokoro_url"));
- const [kokoroApiType, setKokoroApiType] = useState(config("kokoro_api_type"));
+ const [kokoroUrl, setKokoroUrl] = useState(config("kokoro_url"));
+ const [kokoroApiType, setKokoroApiType] = useState<ApiType>(
+ config("kokoro_api_type") === "fastapi" ? "fastapi" : "standard"
+ );
const [kokoroVoice, setKokoroVoice] = useState(config("kokoro_voice"));
@@
case 'kokoro_settings':
return <KokoroSettingsPage
kokoroUrl={kokoroUrl}
kokoroApiType={kokoroApiType}
kokoroVoice={kokoroVoice}
setKokoroVoice={setKokoroVoice}
setKokoroUrl={setKokoroUrl}
setKokoroApiType={setKokoroApiType}
setSettingsUpdated={setSettingsUpdated}
/>This keeps Settings’ local state consistent with the rest of the Kokoro code and prevents accidental invalid values from leaking into the UI. Based on learnings, this restores the strong typing boundary for this config.
Also applies to: 593-602
Make type name Kokoro-specific to avoid naming ambiguity with other backend APIs.
Return empty voices array instead of raw data when response shape is unexpected. Apply defensive validation to both standard and FastAPI parsers, ensuring consistent return type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (3)
src/features/kokoro/kokoro.ts (3)
3-41: Nice API‑type abstraction; consider tighteningparseVoiceListtypingThe
KokoroApiTypeunion plusapiConfigsmap is a clean way to support multiple Kokoro backends and makes it easy to add more in the future. The only small gap is thatparseVoiceListtakesdata: any; you could improve type‑safety by defining explicit response types for the standard and FastAPI shapes (or usingunknownwith runtime narrowing) so that the contract{ voices: string[] }is enforced at the type level as well as at runtime. Not urgent, but it would further reduce “escape hatches” from your otherwise strongly typed surface.Based on learnings, this aligns well with your preference for strong typing;
parseVoiceListis just the one remaininganyhotspot.
43-82: Defensivekokoro_api_typehandling and aligned error checks look solidUsing
rawTypewith a fallback to'standard'avoids crashes from bad config and keeps behavior predictable, and checkingres.okin bothfetchAudioandfetchVoiceListgives consistent, Kokoro‑specific failure semantics. One tiny nit:res.arrayBuffer()is already typed asPromise<ArrayBuffer>in modernlib.dom.d.ts, so if your TS lib target allows it you can likely drop theas anycast on Line 61 and keep the return type purelyArrayBuffer. If the cast is needed for your target, feel free to ignore this.
84-101: Optionally preserve original error when wrapping Kokoro failures
kokoroandkokoroVoiceListcurrently log and then throw a newError("Kokoro TTS API Error"), which hides the original error object from callers (even though it’s in the console). If upstream code ever needs more diagnostic detail, you could either rethrow the original (throw e;) or wrap it with acausewhere supported (e.g.,new Error("Kokoro TTS API Error", { cause: e })). Not required for this PR, but worth considering if you later add richer error handling/UI.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
src/components/settings/KokoroSettingsPage.tsx(3 hunks)src/features/kokoro/kokoro.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-13T07:01:57.942Z
Learnt from: AlphaEcho11
Repo: semperai/amica PR: 213
File: src/features/chat/koboldAiChat.ts:0-0
Timestamp: 2025-08-13T07:01:57.942Z
Learning: AlphaEcho11 prefers strong typing and comprehensive error handling for Tauri invoke calls in frontend TypeScript code. They implemented an OpenAIResponse interface and wrapped invoke calls in try/catch blocks in openAiChat.ts, and expect similar patterns to be applied consistently across similar proxy call implementations.
Applied to files:
src/features/kokoro/kokoro.ts
🧬 Code graph analysis (2)
src/features/kokoro/kokoro.ts (1)
src/utils/config.ts (1)
config(140-155)
src/components/settings/KokoroSettingsPage.tsx (4)
src/features/kokoro/kokoro.ts (1)
KokoroApiType(3-3)src/components/settings/common.tsx (1)
FormRow(74-88)src/i18n/index.ts (1)
t(26-26)src/utils/config.ts (1)
updateConfig(157-172)
🔇 Additional comments (2)
src/components/settings/KokoroSettingsPage.tsx (2)
6-24: Props and effect wiring now match Kokoro API type semanticsImporting
KokoroApiTypehere and using it for bothkokoroApiTypeandsetKokoroApiTypekeeps the settings UI in lock‑step with the backend’s supported modes, and updating theuseEffectdependency array to[kokoroApiType, kokoroUrl]ensures the voice list tracks the active API/URL instead of being fixed at mount time. This is a good, type‑safe clean‑up of the earlier behavior.Also applies to: 28-45
57-77: Based on my analysis, I can confirm the technical validity of the review's suggestions through web search, but I cannot verify the actual implementation details in the codebase due to repository access issues. Here's what I can confirm:Verified Information:
- ✅ The typing suggestion (
React.ChangeEvent<HTMLSelectElement>) is correct per React/TypeScript best practices- ✅ The voice selection concern (stale voice ID after API type switch) is a valid state management pattern that could cause failures
⚠️ Cannot verify: Whether the current code actually has this issue or if voice reset logic is already implementedSince I cannot directly inspect the codebase to confirm the current implementation or verify if these issues actually exist, I must mark this for manual verification:
Reset
kokoroVoicewhen switching API types if the voice is unavailable in the new backend, and tightenonChangetypingWhen the user switches between Standard and FastAPI, the selected
kokoroVoicemay no longer exist in the new backend's voice list, causing TTS calls to fail even though the selector appears in-sync. After fetching the new voice list, verify that the currentkokoroVoiceis valid; if not, automatically reset it to a safe default (e.g., the first voice in the list) and persist viasetKokoroVoice/updateConfig.Additionally, narrow the select element's event typing:
onChange={(event: React.ChangeEvent<HTMLSelectElement>) => { const value = event.target.value as KokoroApiType; setKokoroApiType(value); updateConfig("kokoro_api_type", value); setSettingsUpdated(true); }}This maintains proper type safety and ensures only valid
KokoroApiTypevalues flow through the handler.
Add explicit type annotation to kokoroApiType useState and apply defensive fallback to ensure invalid config values default to 'standard', maintaining type safety across the component boundary.
Standard parser now handles {"voices":[...]} objects and bare [...] arrays. FastAPI parser handles both data.data (objects with id) and data.voices (string array) formats. Both default to empty array on unexpected format.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (2)
src/features/kokoro/kokoro.ts (2)
49-68: Remove unnecessary type assertion.Line 67 casts the
ArrayBufferresult toany, butres.arrayBuffer()already returnsPromise<ArrayBuffer>. The cast is redundant and weakens type safety.Based on learnings, strong typing is preferred in this codebase.
Apply this diff:
- return (await res.arrayBuffer()) as any; + return await res.arrayBuffer();
90-98: Consider preserving error context in exception handling (optional).The catch block throws a generic error message, which discards the original error details. While the error is logged to console, including the original error message or cause in the thrown error would aid debugging.
One option:
} catch (e) { console.error('ERROR', e); - throw new Error("Kokoro TTS API Error"); + throw new Error(`Kokoro TTS API Error: ${e instanceof Error ? e.message : String(e)}`); }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/features/kokoro/kokoro.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-13T07:01:57.942Z
Learnt from: AlphaEcho11
Repo: semperai/amica PR: 213
File: src/features/chat/koboldAiChat.ts:0-0
Timestamp: 2025-08-13T07:01:57.942Z
Learning: AlphaEcho11 prefers strong typing and comprehensive error handling for Tauri invoke calls in frontend TypeScript code. They implemented an OpenAIResponse interface and wrapped invoke calls in try/catch blocks in openAiChat.ts, and expect similar patterns to be applied consistently across similar proxy call implementations.
Applied to files:
src/features/kokoro/kokoro.ts
🧬 Code graph analysis (1)
src/features/kokoro/kokoro.ts (2)
src/features/chat/chat.ts (1)
fetchAudio(638-707)src/utils/config.ts (1)
config(140-155)
🔇 Additional comments (3)
src/features/kokoro/kokoro.ts (3)
5-26: Clean API abstraction with good extensibility.The
apiConfigspattern effectively decouples the two API variants and makes it straightforward to add additional implementations in the future. The defensive parsing inparseVoiceListfor the standard API handles multiple response formats gracefully.
70-88: Good defensive programming and error handling.The defensive defaulting for
apiTypeand theresponse.okcheck ensure robust behavior even with unexpected config values or API failures. The implementation is consistent withfetchAudio.
100-107: LGTM! Good error handling pattern.The function correctly delegates to
fetchVoiceList()and preserves the original error when re-throwing, which aids debugging.
|
Boy, that CodeRabbit thing surely is picky. :D |
Validate voice entries in parseVoiceList before mapping, filtering out undefined/invalid entries. Remove unnecessary type assertion from arrayBuffer() call. Preserve original error context in exception messages for better debugging.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/features/kokoro/kokoro.ts (1)
58-60: Consider extracting the API type validation logic (optional).The API type validation with defensive default appears in both
fetchAudioandfetchVoiceList. Extracting it into a small helper function would reduce duplication and make future changes easier.For example:
function getValidatedApiType(): KokoroApiType { const rawType = config("kokoro_api_type"); return rawType === "fastapi" ? "fastapi" : "standard"; }Then use it in both functions:
async function fetchAudio(message: string): Promise<ArrayBuffer> { - const rawType = config("kokoro_api_type"); - const apiType: KokoroApiType = rawType === "fastapi" ? "fastapi" : "standard"; + const apiType = getValidatedApiType(); const apiConfig = apiConfigs[apiType]; // ... } async function fetchVoiceList(): Promise<{ voices: string[] }> { - const rawType = config("kokoro_api_type"); - const apiType: KokoroApiType = rawType === "fastapi" ? "fastapi" : "standard"; + const apiType = getValidatedApiType(); const apiConfig = apiConfigs[apiType]; // ... }Also applies to: 79-80
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
src/features/kokoro/kokoro.ts(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-13T07:01:57.942Z
Learnt from: AlphaEcho11
Repo: semperai/amica PR: 213
File: src/features/chat/koboldAiChat.ts:0-0
Timestamp: 2025-08-13T07:01:57.942Z
Learning: AlphaEcho11 prefers strong typing and comprehensive error handling for Tauri invoke calls in frontend TypeScript code. They implemented an OpenAIResponse interface and wrapped invoke calls in try/catch blocks in openAiChat.ts, and expect similar patterns to be applied consistently across similar proxy call implementations.
Applied to files:
src/features/kokoro/kokoro.ts
🧬 Code graph analysis (1)
src/features/kokoro/kokoro.ts (2)
src/features/chat/chat.ts (1)
fetchAudio(638-707)src/utils/config.ts (1)
config(140-155)
🔇 Additional comments (6)
src/features/kokoro/kokoro.ts (6)
3-4: Clean type definition.The
KokoroApiTypeunion type is well-defined and provides clear type safety for API mode selection.
5-55: Excellent configuration abstraction with robust validation.The
apiConfigsmap provides a clean, extensible structure for supporting multiple API variants. TheparseVoiceListimplementations include comprehensive validation that filters out invalid entries, successfully addressing previous review concerns about voice ID safety.
57-76: Well-structured with proper error handling.The
fetchAudiofunction correctly implements the defensive API type default and response validation, addressing previous review concerns. Error handling is consistent and informative.
78-96: Consistent implementation with proper error handling.The
fetchVoiceListfunction correctly implements response validation and defensive API type defaults, addressing previous review concerns. The structure mirrorsfetchAudiofor consistency.
98-106: LGTM!Clean implementation with informative error handling. The enhanced error message includes the original error details, which aids debugging.
108-115: LGTM!Clean delegation to
fetchVoiceListwith appropriate error handling.
Add support for Kokoro-FastAPI alongside the standard Kokoro implementation. Users can now toggle between standard and FastAPI (OpenAI-compatible) endpoints in settings.
Changes:
The implementation allows seamless switching between:
Summary by CodeRabbit
New Features
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.