-
Notifications
You must be signed in to change notification settings - Fork 2.1k
feat: enable automatic injection of the current timestamp into user prompts #3268
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: main
Are you sure you want to change the base?
Changes from all commits
8cefb7e
205c76a
5d82b63
f1e2328
a1a85db
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import { request } from "../request"; | ||
|
|
||
| export interface TimeAwarenessConfig { | ||
| enabled: boolean; | ||
| format: string | null; | ||
| } | ||
|
|
||
| export const timeAwarenessApi = { | ||
| getTimeAwareness: () => | ||
| request<TimeAwarenessConfig>("/config/time-awareness"), | ||
|
|
||
| updateTimeAwareness: (config: Partial<TimeAwarenessConfig>) => | ||
| request<TimeAwarenessConfig>("/config/time-awareness", { | ||
| method: "PUT", | ||
| body: JSON.stringify(config), | ||
| }), | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,220 @@ | ||
| import { useState, useEffect, useCallback } from "react"; | ||
| import { Switch, Card, Input, Tag, Tooltip } from "@agentscope-ai/design"; | ||
| import { Typography, Space } from "antd"; | ||
| import { | ||
| ClockCircleOutlined, | ||
| InfoCircleOutlined, | ||
| CheckCircleOutlined, | ||
| } from "@ant-design/icons"; | ||
| import { useTranslation } from "react-i18next"; | ||
| import api from "../../../../api"; | ||
| import { useAppMessage } from "../../../../hooks/useAppMessage"; | ||
| import styles from "../index.module.less"; | ||
|
|
||
| const { Text, Paragraph } = Typography; | ||
|
|
||
| interface TimeAwarenessCardProps {} | ||
|
|
||
| export function TimeAwarenessCard(_props: TimeAwarenessCardProps) { | ||
| const { t } = useTranslation(); | ||
| const { message } = useAppMessage(); | ||
|
|
||
| const [enabled, setEnabled] = useState(false); | ||
| const [format, setFormat] = useState<string>(""); | ||
| const [loading, setLoading] = useState(true); | ||
| const [saving, setSaving] = useState(false); | ||
|
|
||
| const fetchConfig = useCallback(async () => { | ||
| setLoading(true); | ||
| try { | ||
| const config = await api.getTimeAwareness(); | ||
| setEnabled(config.enabled); | ||
| setFormat(config.format || ""); | ||
| } catch (err) { | ||
| console.error("Failed to fetch time awareness config:", err); | ||
| message.error(t("timeAwareness.loadFailed")); | ||
| } finally { | ||
| setLoading(false); | ||
| } | ||
| }, [t, message]); | ||
|
|
||
| useEffect(() => { | ||
| fetchConfig(); | ||
| }, [fetchConfig]); | ||
|
|
||
| const handleToggle = useCallback( | ||
| async (checked: boolean) => { | ||
| setSaving(true); | ||
| try { | ||
| await api.updateTimeAwareness({ | ||
| enabled: checked, | ||
| format: format || null, | ||
| }); | ||
| setEnabled(checked); | ||
| message.success( | ||
| checked | ||
| ? t("timeAwareness.enableSuccess") | ||
| : t("timeAwareness.disableSuccess"), | ||
| ); | ||
| } catch (err) { | ||
| console.error("Failed to update time awareness:", err); | ||
| message.error(t("timeAwareness.saveFailed")); | ||
| // Revert on error | ||
| setEnabled(!checked); | ||
| } finally { | ||
| setSaving(false); | ||
| } | ||
| }, | ||
| [format, t, message], | ||
| ); | ||
|
|
||
| const handleFormatChange = useCallback( | ||
| async (e: React.ChangeEvent<HTMLInputElement>) => { | ||
| const newFormat = e.target.value; | ||
| setFormat(newFormat); | ||
|
|
||
| if (enabled) { | ||
| setSaving(true); | ||
| try { | ||
| await api.updateTimeAwareness({ | ||
| enabled, | ||
| format: newFormat || null, | ||
| }); | ||
| message.success(t("timeAwareness.formatSaveSuccess")); | ||
| } catch (err) { | ||
| console.error("Failed to update time format:", err); | ||
| message.error(t("timeAwareness.formatSaveFailed")); | ||
| // Revert on error | ||
| setFormat(format); | ||
| } finally { | ||
| setSaving(false); | ||
| } | ||
| } | ||
| }, | ||
| [enabled, format, t, message], | ||
| ); | ||
|
|
||
| if (loading) { | ||
| return ( | ||
| <Card className={styles.formCard} loading={loading}> | ||
| <div style={{ height: 200 }} /> | ||
| </Card> | ||
| ); | ||
| } | ||
|
|
||
| return ( | ||
| <Card | ||
| className={styles.formCard} | ||
| title={ | ||
| <Space> | ||
| <ClockCircleOutlined /> | ||
| <span>{t("timeAwareness.title")}</span> | ||
| {enabled ? ( | ||
| <Tag color="success" icon={<CheckCircleOutlined />}> | ||
| {t("timeAwareness.enabled")} | ||
| </Tag> | ||
| ) : ( | ||
| <Tag>{t("timeAwareness.disabled")}</Tag> | ||
| )} | ||
| </Space> | ||
| } | ||
| > | ||
| <div className={styles.timeAwarenessContainer}> | ||
| <Paragraph className={styles.description}> | ||
| {t("timeAwareness.description")} | ||
| </Paragraph> | ||
|
|
||
| <div className={styles.switchRow}> | ||
| <Space direction="vertical" size="small" style={{ flex: 1 }}> | ||
| <Text strong>{t("timeAwareness.enableLabel")}</Text> | ||
| <Text type="secondary" className={styles.hintText}> | ||
| {t("timeAwareness.enableHint")} | ||
| </Text> | ||
| </Space> | ||
|
|
||
| <Switch | ||
| checked={enabled} | ||
| onChange={handleToggle} | ||
| loading={saving} | ||
| checkedChildren={t("timeAwareness.on")} | ||
| unCheckedChildren={t("timeAwareness.off")} | ||
| style={{ marginLeft: "auto" }} | ||
| /> | ||
| </div> | ||
|
|
||
| {enabled && ( | ||
| <div className={styles.formatSection}> | ||
| <Space direction="vertical" size="small" style={{ width: "100%" }}> | ||
| <div style={{ display: "flex", alignItems: "center", gap: 8 }}> | ||
| <Text strong>{t("timeAwareness.formatLabel")}</Text> | ||
| <Tooltip title={t("timeAwareness.formatTooltip")}> | ||
| <InfoCircleOutlined style={{ color: "#999" }} /> | ||
| </Tooltip> | ||
| </div> | ||
|
|
||
| <Input | ||
| value={format} | ||
| onChange={handleFormatChange} | ||
| placeholder={t("timeAwareness.formatPlaceholder")} | ||
| disabled={!enabled || saving} | ||
| allowClear | ||
| suffix={ | ||
| <Text type="secondary" style={{ fontSize: 12 }}> | ||
| strftime | ||
| </Text> | ||
| } | ||
| /> | ||
|
|
||
| <div className={styles.formatExamples}> | ||
| <Text type="secondary" style={{ fontSize: 12 }}> | ||
| {t("timeAwareness.examples")}: | ||
| </Text> | ||
| <Space size={4} wrap> | ||
| <Tag | ||
| onClick={() => setFormat("%Y-%m-%d %H:%M:%S")} | ||
| style={{ cursor: "pointer" }} | ||
| > | ||
| Default | ||
| </Tag> | ||
| <Tag | ||
| onClick={() => setFormat("%Y-%m-%d %H:%M")} | ||
| style={{ cursor: "pointer" }} | ||
| > | ||
| Compact | ||
| </Tag> | ||
| <Tag | ||
| onClick={() => setFormat("%H:%M")} | ||
| style={{ cursor: "pointer" }} | ||
| > | ||
| Time Only | ||
| </Tag> | ||
| </Space> | ||
|
Comment on lines
+173
to
+191
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clicking the quick template tags only updates the local |
||
| </div> | ||
| </Space> | ||
|
|
||
| <div className={styles.previewSection}> | ||
| <Text strong>{t("timeAwareness.previewLabel")}</Text> | ||
| <div className={styles.previewBox}> | ||
| <Text code style={{ fontSize: 13 }}> | ||
| {[ | ||
| t("timeAwareness.previewPrefix"), | ||
| ": 2026-04-11 14:30:45 Asia/Shanghai (Saturday)", | ||
| ].join("")} | ||
| </Text> | ||
| <Text type="secondary" style={{ fontSize: 12, marginTop: 4 }}> | ||
| {t("timeAwareness.previewHint")} | ||
| </Text> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| )} | ||
|
|
||
| {!enabled && ( | ||
| <div className={styles.disabledHint}> | ||
| <Text type="warning">⚠️ {t("timeAwareness.disabledHint")}</Text> | ||
| </div> | ||
| )} | ||
| </div> | ||
| </Card> | ||
| ); | ||
| } | ||
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.
The
handleFormatChangefunction triggers an API call on every keystroke (onChange). This can lead to a high volume of unnecessary network requests and potential race conditions if the user types quickly. It is recommended to debounce the API call or trigger it ononBlur.