feat(config,usage): integrate usage persistence settings and runtime status UI#150
feat(config,usage): integrate usage persistence settings and runtime status UI#150shaoyuanyu wants to merge 2 commits intorouter-for-me:mainfrom
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly enhances the user experience by integrating usage statistics persistence capabilities directly into the Web UI. Users can now easily configure how usage data is saved and loaded through a dedicated section in the Config panel. Furthermore, the Usage page has been improved to provide real-time feedback on the persistence status, including the last saved timestamp, and to automatically save data upon refresh, ensuring data integrity and usability. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
|
should be based on router-for-me/CLIProxyAPI#2125 |
There was a problem hiding this comment.
Code Review
This pull request effectively integrates usage persistence configuration and runtime status into the UI. The changes are comprehensive, covering configuration, API services, state management, and UI components. The code is well-structured and the new features are implemented as described. I have a couple of suggestions to improve code maintainability and efficiency by refactoring some repetitive parsing logic and optimizing API calls in the state store. Overall, this is a solid contribution.
| const parseUsagePersistenceStatus = (payload: unknown): UsagePersistenceStatus => { | ||
| if (!payload || typeof payload !== 'object') return {}; | ||
| const record = payload as Record<string, unknown>; | ||
| return { | ||
| enabled: typeof record.enabled === 'boolean' ? record.enabled : undefined, | ||
| filePath: | ||
| typeof record['file-path'] === 'string' | ||
| ? record['file-path'] | ||
| : typeof record.path === 'string' | ||
| ? record.path | ||
| : typeof record.filePath === 'string' | ||
| ? record.filePath | ||
| : undefined, | ||
| intervalSeconds: | ||
| typeof record['interval-seconds'] === 'number' | ||
| ? record['interval-seconds'] | ||
| : typeof record.interval_seconds === 'number' | ||
| ? record.interval_seconds | ||
| : typeof record.intervalSeconds === 'number' | ||
| ? record.intervalSeconds | ||
| : undefined, | ||
| lastLoadedAt: | ||
| typeof record['last-loaded-at'] === 'string' | ||
| ? record['last-loaded-at'] | ||
| : typeof record.last_loaded_at === 'string' | ||
| ? record.last_loaded_at | ||
| : typeof record.lastLoadedAt === 'string' | ||
| ? record.lastLoadedAt | ||
| : undefined, | ||
| lastSavedAt: | ||
| typeof record['last-saved-at'] === 'string' | ||
| ? record['last-saved-at'] | ||
| : typeof record.last_saved_at === 'string' | ||
| ? record.last_saved_at | ||
| : typeof record.lastSavedAt === 'string' | ||
| ? record.lastSavedAt | ||
| : undefined, | ||
| saveCount: | ||
| typeof record['save-count'] === 'number' | ||
| ? record['save-count'] | ||
| : typeof record.saveCount === 'number' | ||
| ? record.saveCount | ||
| : undefined, | ||
| loadCount: | ||
| typeof record['load-count'] === 'number' | ||
| ? record['load-count'] | ||
| : typeof record.loadCount === 'number' | ||
| ? record.loadCount | ||
| : undefined, | ||
| lastLoadAdded: | ||
| typeof record['last-load-added'] === 'number' | ||
| ? record['last-load-added'] | ||
| : typeof record.lastLoadAdded === 'number' | ||
| ? record.lastLoadAdded | ||
| : undefined, | ||
| lastLoadSkipped: | ||
| typeof record['last-load-skipped'] === 'number' | ||
| ? record['last-load-skipped'] | ||
| : typeof record.lastLoadSkipped === 'number' | ||
| ? record.lastLoadSkipped | ||
| : undefined, | ||
| lastError: | ||
| typeof record['last-error'] === 'string' | ||
| ? record['last-error'] | ||
| : typeof record.last_error === 'string' | ||
| ? record.last_error | ||
| : typeof record.lastError === 'string' | ||
| ? record.lastError | ||
| : undefined | ||
| }; | ||
| }; |
There was a problem hiding this comment.
The parsing functions parseUsagePersistenceConfig, parseUsagePersistenceStatus, and parseUsagePersistenceLoadResult contain repetitive code for checking multiple possible property names (e.g., file-path, filePath). This can be simplified by using a helper function to reduce boilerplate and improve maintainability.
Here's a suggested refactoring for parseUsagePersistenceStatus. A similar pattern can be applied to the other parsing functions in this file.
const parseUsagePersistenceStatus = (payload: unknown): UsagePersistenceStatus => {
if (!payload || typeof payload !== 'object') return {};
const record = payload as Record<string, unknown>;
const getTypedValue = <T extends string | number | boolean>(
keys: string[],
type: 'string' | 'number' | 'boolean'
): T | undefined => {
for (const key of keys) {
const value = record[key];
if (typeof value === type) {
return value as T;
}
}
return undefined;
};
return {
enabled: getTypedValue<boolean>(['enabled'], 'boolean'),
filePath: getTypedValue<string>(['file-path', 'path', 'filePath'], 'string'),
intervalSeconds: getTypedValue<number>(
['interval-seconds', 'interval_seconds', 'intervalSeconds'],
'number'
),
lastLoadedAt: getTypedValue<string>(
['last-loaded-at', 'last_loaded_at', 'lastLoadedAt'],
'string'
),
lastSavedAt: getTypedValue<string>(['last-saved-at', 'last_saved_at', 'lastSavedAt'], 'string'),
saveCount: getTypedValue<number>(['save-count', 'saveCount'], 'number'),
loadCount: getTypedValue<number>(['load-count', 'loadCount'], 'number'),
lastLoadAdded: getTypedValue<number>(['last-load-added', 'lastLoadAdded'], 'number'),
lastLoadSkipped: getTypedValue<number>(['last-load-skipped', 'lastLoadSkipped'], 'number'),
lastError: getTypedValue<string>(['last-error', 'last_error', 'lastError'], 'string')
};
};| loadUsagePersistenceState: async () => { | ||
| set({ usagePersistenceLoading: true, usagePersistenceError: null }); | ||
| try { | ||
| const [configRes, statusRes] = await Promise.all([ | ||
| usageApi.getUsagePersistenceConfig(), | ||
| usageApi.getUsagePersistenceStatus(), | ||
| ]); | ||
| set({ | ||
| usagePersistenceConfig: configRes.config, | ||
| usagePersistenceStatus: statusRes, | ||
| usagePersistenceLoading: false, | ||
| usagePersistenceError: null, | ||
| }); | ||
| } catch (error: unknown) { | ||
| set({ | ||
| usagePersistenceLoading: false, | ||
| usagePersistenceError: getErrorMessage(error), | ||
| }); | ||
| throw error; | ||
| } | ||
| }, |
There was a problem hiding this comment.
This function makes two separate API calls to fetch the persistence config and status. However, usageApi.getUsagePersistenceConfig() already returns both the configuration and the runtime status in a single response. You can optimize this by making only one API call to improve efficiency and reduce network overhead.
A similar optimization can be applied to the updateUsagePersistenceConfig function on line 173.
loadUsagePersistenceState: async () => {
set({ usagePersistenceLoading: true, usagePersistenceError: null });
try {
const { config, status } = await usageApi.getUsagePersistenceConfig();
set({
usagePersistenceConfig: config,
usagePersistenceStatus: status,
usagePersistenceLoading: false,
usagePersistenceError: null,
});
} catch (error: unknown) {
set({
usagePersistenceLoading: false,
usagePersistenceError: getErrorMessage(error),
});
throw error;
}
},
What changed1) Usage persistence config/status schema update
2) Chart data source optimization
Why
Impact
|
Summary
This PR integrates usage persistence configuration and runtime status into the Web UI, and improves usability by saving immediately on Usage page refresh and showing “last saved” information.
Motivation
After introducing backend persistence capability, users need a clear and centralized UI to configure it (in Config panel) and observe runtime behavior (in Usage page).
Changes
VisualConfigEditor) now supports:usage-persistence.usage-persistence.zh-CN/en/ru.Backward Compatibility
Validation
tsc && vite build).