支持部分agent实时更新上下文用量信息
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
"use client"
|
||||
|
||||
import { useCallback, useSyncExternalStore } from "react"
|
||||
import { Coins } from "lucide-react"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { useSessionStats } from "@/contexts/session-stats-context"
|
||||
import { useConnectionStore } from "@/contexts/acp-connections-context"
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
@@ -31,13 +33,49 @@ function formatPercent(percent: number | null): string {
|
||||
|
||||
export function StatusBarTokens() {
|
||||
const t = useTranslations("Folder.statusBar.tokens")
|
||||
const store = useConnectionStore()
|
||||
const { sessionStats } = useSessionStats()
|
||||
const usage = sessionStats?.total_usage
|
||||
|
||||
const contextUsed = sessionStats?.context_window_used_tokens ?? null
|
||||
const contextMax = sessionStats?.context_window_max_tokens ?? null
|
||||
const subscribeActiveKey = useCallback(
|
||||
(cb: () => void) => store.subscribeActiveKey(cb),
|
||||
[store]
|
||||
)
|
||||
const getActiveKey = useCallback(() => store.getActiveKey(), [store])
|
||||
const activeKey = useSyncExternalStore(
|
||||
subscribeActiveKey,
|
||||
getActiveKey,
|
||||
getActiveKey
|
||||
)
|
||||
|
||||
const subscribeConn = useCallback(
|
||||
(cb: () => void) => {
|
||||
if (!activeKey) return () => {}
|
||||
return store.subscribeKey(activeKey, cb)
|
||||
},
|
||||
[store, activeKey]
|
||||
)
|
||||
const getConnSnapshot = useCallback(
|
||||
() => (activeKey ? store.getConnection(activeKey) : undefined),
|
||||
[store, activeKey]
|
||||
)
|
||||
const activeConn = useSyncExternalStore(
|
||||
subscribeConn,
|
||||
getConnSnapshot,
|
||||
getConnSnapshot
|
||||
)
|
||||
|
||||
const liveContextUsed = activeConn?.usage?.used ?? null
|
||||
const liveContextMax = activeConn?.usage?.size ?? null
|
||||
|
||||
const contextUsed =
|
||||
liveContextUsed ?? sessionStats?.context_window_used_tokens ?? null
|
||||
const contextMax =
|
||||
liveContextMax ?? sessionStats?.context_window_max_tokens ?? null
|
||||
const contextPercentRaw =
|
||||
sessionStats?.context_window_usage_percent ??
|
||||
(liveContextUsed != null && liveContextMax != null && liveContextMax > 0
|
||||
? (liveContextUsed / liveContextMax) * 100
|
||||
: sessionStats?.context_window_usage_percent) ??
|
||||
(contextUsed != null && contextMax != null && contextMax > 0
|
||||
? (contextUsed / contextMax) * 100
|
||||
: null)
|
||||
|
||||
@@ -33,6 +33,7 @@ import type {
|
||||
PermissionOptionInfo,
|
||||
SessionConfigOptionInfo,
|
||||
SessionModeStateInfo,
|
||||
SessionUsageUpdateInfo,
|
||||
FixAction,
|
||||
PromptCapabilitiesInfo,
|
||||
PromptInputBlock,
|
||||
@@ -88,6 +89,7 @@ export interface ConnectionState {
|
||||
modes: SessionModeStateInfo | null
|
||||
configOptions: SessionConfigOptionInfo[] | null
|
||||
availableCommands: AvailableCommandInfo[] | null
|
||||
usage: SessionUsageUpdateInfo | null
|
||||
liveMessage: LiveMessage | null
|
||||
pendingPermission: PendingPermission | null
|
||||
error: string | null
|
||||
@@ -177,6 +179,11 @@ type Action =
|
||||
contextKey: string
|
||||
commands: AvailableCommandInfo[]
|
||||
}
|
||||
| {
|
||||
type: "USAGE_UPDATE"
|
||||
contextKey: string
|
||||
usage: SessionUsageUpdateInfo
|
||||
}
|
||||
|
||||
type StreamingAction =
|
||||
| { type: "CONTENT_DELTA"; contextKey: string; text: string }
|
||||
@@ -441,6 +448,7 @@ function connectionsReducer(
|
||||
modes: null,
|
||||
configOptions: null,
|
||||
availableCommands: null,
|
||||
usage: null,
|
||||
liveMessage: null,
|
||||
pendingPermission: null,
|
||||
error: null,
|
||||
@@ -883,6 +891,23 @@ function connectionsReducer(
|
||||
return next
|
||||
}
|
||||
|
||||
case "USAGE_UPDATE": {
|
||||
const conn = state.get(action.contextKey)
|
||||
if (!conn) return state
|
||||
if (
|
||||
conn.usage?.used === action.usage.used &&
|
||||
conn.usage?.size === action.usage.size
|
||||
) {
|
||||
return state
|
||||
}
|
||||
const next = new Map(state)
|
||||
next.set(action.contextKey, {
|
||||
...conn,
|
||||
usage: action.usage,
|
||||
})
|
||||
return next
|
||||
}
|
||||
|
||||
default:
|
||||
return state
|
||||
}
|
||||
@@ -1395,6 +1420,17 @@ export function AcpConnectionsProvider({ children }: { children: ReactNode }) {
|
||||
commands: e.commands,
|
||||
})
|
||||
break
|
||||
case "usage_update":
|
||||
flushStreamingQueue()
|
||||
dispatch({
|
||||
type: "USAGE_UPDATE",
|
||||
contextKey,
|
||||
usage: {
|
||||
used: e.used,
|
||||
size: e.size,
|
||||
},
|
||||
})
|
||||
break
|
||||
}
|
||||
},
|
||||
[dispatch, enqueueStreamingAction, flushStreamingQueue, t]
|
||||
|
||||
@@ -402,6 +402,11 @@ export interface AvailableCommandInfo {
|
||||
input_hint?: string | null
|
||||
}
|
||||
|
||||
export interface SessionUsageUpdateInfo {
|
||||
used: number
|
||||
size: number
|
||||
}
|
||||
|
||||
// ACP events pushed from Rust backend (discriminated by "type" field)
|
||||
export type AcpEvent =
|
||||
| { type: "content_delta"; connection_id: string; text: string }
|
||||
@@ -481,6 +486,12 @@ export type AcpEvent =
|
||||
connection_id: string
|
||||
commands: AvailableCommandInfo[]
|
||||
}
|
||||
| {
|
||||
type: "usage_update"
|
||||
connection_id: string
|
||||
used: number
|
||||
size: number
|
||||
}
|
||||
|
||||
// Connection info returned by acp_list_connections
|
||||
export interface ConnectionInfo {
|
||||
|
||||
Reference in New Issue
Block a user