状态管理(中文译文)
原始 DeepWiki 页面:https://deepwiki.com/onyx-dot-app/onyx/10.3-state-management
翻译时间:2026-05-27T08:44:55.784Z
翻译模型:deepseek-chat
原文字符数:13445
项目:Onyx (onyx)
---
状态管理
相关源文件
以下文件为本维基页面的生成提供了上下文:
.github/workflows/nightly-external-dependency-unit-tests.yml.pre-commit-config.yamlbackend/onyx/llm/litellm_singleton/monkey_patches.pybackend/pytest.inibackend/requirements/README.mdbackend/requirements/default.txtbackend/requirements/dev.txtbackend/requirements/ee.txtbackend/requirements/model_server.txtbackend/tests/external_dependency_unit/conftest.pybackend/tests/external_dependency_unit/file_store/test_file_store_non_mocked.pybackend/tests/external_dependency_unit/llm/test_ollama_streaming.pybackend/tests/external_dependency_unit/llm/test_openai_responses_api.pybackend/tests/unit/onyx/llm/test_factory.pybackend/tests/unit/onyx/llm/test_litellm_monkey_patches.pypyproject.tomluv.lockweb/lib/opal/src/components/README.mdweb/lib/opal/src/core/README.mdweb/lib/opal/src/core/index.tsweb/lib/opal/src/core/interactive/README.md- [web/src/app/admin/connector/[ccPairId]/InlineFileManagement.tsx](web/src/app/admin/connector/[ccPairId]/InlineFileManagement.tsx)
web/src/app/components/nrf/SettingsPanel.tsxweb/src/components/context/NRFPreferencesContext.tsxweb/src/lib/constants/chatBackgrounds.tsweb/src/lib/hooks.tsweb/src/providers/AppBackgroundProvider.tsxweb/src/refresh-pages/SettingsPage.tsxweb/src/sections/cards/DocumentSetCard.tsxweb/src/sections/cards/FileCard.tsx
目的与范围
本文档介绍了 Onyx 应用中的前端状态管理模式,重点涵盖自定义 React 钩子(Hook)和状态管理工具,用于处理数据获取、缓存以及客户端状态同步。这些模式主要在 Next.js 前端层实现,并通过 REST 端点与 FastAPI 后端进行协调。
有关后端 API 服务器架构,请参见 8.2 API 服务器架构。有关前端应用结构,请参见 10.1 应用结构与配置。有关状态管理中使用的类型定义,请参见 10.4 类型系统与数据模型。
---
基于 SWR 的数据获取
Onyx 前端使用 SWR(stale-while-revalidate,过期时重新验证) 作为主要的数据获取和缓存库。SWR 提供了自动缓存、重新验证和乐观更新功能,且样板代码极少。
核心 SWR 钩子
应用提供了大量领域特定的钩子,它们封装了 SWR 以处理不同类型的数据:
| 钩子 | 端点 | 用途 | 刷新能力 |
|---|---|---|---|
usePublicCredentials | /api/manage/admin/credential | 获取连接器的凭证 | refreshCredentials() |
useConnectorStatus | /api/manage/admin/connector/status | 获取完整的连接器状态,30 秒自动刷新 | refreshIndexingStatus() |
useBasicConnectorStatus | /api/manage/connector-status | 获取简化的连接器状态 | refreshIndexingStatus() |
useFederatedConnectors | /api/federated | 获取联邦连接器的详细信息 | refreshFederatedConnectors() |
useLabels | /api/persona/labels | 获取和管理角色标签 | refreshLabels() |
useUserGroups | /api/manage/admin/user-group | 获取用户组(仅企业版) | refreshUserGroups() |
useLLMProviders | /api/llm/provider | 获取用户可访问的大语言模型(LLM)提供商 | refetch() |
useAdminLLMProviders | /api/admin/llm/provider | 获取管理员可查看的完整大语言模型(LLM)提供商视图 | refetch() |
来源:web/src/lib/hooks.ts:44-55, web/src/lib/hooks.ts:209-225, web/src/lib/hooks.ts:227-237, web/src/lib/hooks.ts:239-249, web/src/lib/hooks.ts:265-275, web/src/lib/hooks.ts:277-287
SWR 模式实现
下图展示了使用 SWR 钩子时,React 组件与后端之间的数据流。
SWR 数据流
graph TB
Component["React 组件"]
Hook["自定义 SWR 钩子<br/>(例如 usePublicCredentials)"]
SWR["useSWR"]
Cache["SWR 缓存"]
Fetcher["errorHandlingFetcher"]
API["FastAPI 后端<br/>/api/manage/admin/credential"]
Component -->|"调用"| Hook
Hook -->|"useSWR(url, fetcher)"| SWR
SWR -->|"检查"| Cache
Cache -->|"缓存未命中或过期"| Fetcher
Fetcher -->|"HTTP GET"| API
API -->|"响应"| Fetcher
Fetcher -->|"更新"| Cache
Cache -->|"返回数据"| Component
Hook -->|"提供 mutate()"| Component
Component -->|"手动刷新"| Cache
来源:web/src/lib/hooks.ts:14-15, web/src/lib/hooks.ts:44-55, web/src/lib/hooks.ts:209-225
示例:usePublicCredentials
usePublicCredentials 钩子展示了封装 useSWR 以提供领域特定接口的标准模式:
web/src/lib/hooks.ts:44-55
主要特性:
- 使用
errorHandlingFetcher实现一致的错误处理web/src/lib/hooks.ts:15-15。 - 返回包含
data、error、isLoading的 SWR 响应。 - 提供
refreshCredentials()方法,通过mutate()实现手动缓存失效web/src/lib/hooks.ts:53-53。 - 使用来自
SWR_KEYS的集中式键web/src/lib/hooks.ts:47-47。
来源:web/src/lib/hooks.ts:44-55, web/src/lib/swr-keys.ts:1-20
---
复杂状态:带分页的连接器索引
useConnectorIndexingStatusWithPagination 钩子实现了一个复杂的分页系统,该系统在支持全局过滤器和自动刷新的同时,维护每个数据源的页面状态。
架构
分页与合并逻辑
graph TB
Component["管理组件"]
Hook["useConnectorIndexingStatusWithPagination"]
MainSWR["主 SWR 请求<br/>(所有数据源,当前页面)"]
SourcePages["sourcePages 状态<br/>Record<ValidSources, number>"]
MergedData["mergedData 状态<br/>ConnectorIndexingStatusLiteResponse[]"]
Component -->|"过滤器,启用状态"| Hook
Hook -->|"维护"| SourcePages
Hook -->|"维护"| MergedData
Hook -->|"useSWR 带 30 秒刷新"| MainSWR
MainSWR -->|"获取所有数据源"| API["POST /api/manage/admin/connector/indexing-status"]
MainSWR -->|"更新"| MergedData
Component -->|"handlePageChange(source, page)"| Hook
Hook -->|"更新"| SourcePages
Hook -->|"获取单个数据源"| API
Hook -->|"合并到"| MergedData
Component -->|"refreshAllData()"| Hook
Hook -->|"变更"| MainSWR
Component -->|"resetPagination()"| Hook
Hook -->|"清除"| SourcePages
来源:web/src/lib/hooks.ts:89-207
状态管理策略
该钩子管理三个关键状态:
- sourcePages:
Record<ValidSources, number>- 跟踪每个数据源的当前页码web/src/lib/hooks.ts:96-98。 - mergedData:
ConnectorIndexingStatusLiteResponse[]- 所有数据源的聚合连接器状态web/src/lib/hooks.ts:99-101。 - sourceLoadingStates:
Record<ValidSources, boolean>- 每个数据源的加载指示器web/src/lib/hooks.ts:103-105。
实现中使用 useRef 在 SWR 获取期间维护分页状态的稳定引用,以避免依赖循环:
web/src/lib/hooks.ts:108-109
来源:web/src/lib/hooks.ts:89-207
---
过滤器管理系统
useFilters 模式提供了一个全面的过滤器管理系统,用于搜索和文档浏览,并支持 URL 同步。
FilterManager 接口
过滤器状态结构
graph LR
FilterManager["FilterManager 接口"]
TimeRange["timeRange: DateRangePickerValue | null"]
Sources["selectedSources: SourceMetadata[]"]
DocSets["selectedDocumentSets: string[]"]
Tags["selectedTags: Tag[]"]
FilterManager --> TimeRange
FilterManager --> Sources
FilterManager --> DocSets
FilterManager --> Tags
FilterManager -->|"getFilterString()"| QueryString["URL 查询字符串"]
FilterManager -->|"buildFiltersFromQueryString()"| URLParsing["解析 URL 参数"]
FilterManager -->|"clearFilters()"| Reset["重置所有过滤器"]
来源:web/src/lib/hooks.ts:340-479
URL 序列化
该钩子提供了双向 URL 同步:
序列化 通过 getFilterString():
- 将过滤器状态转换为 URL 参数:
&from=...&to=...&sources=...&documentSets=...&tags=...web/src/lib/hooks.ts:369-389。 - 对所有值进行 URL 编码以确保安全。
反序列化 通过 buildFiltersFromQueryString():
- 将 URL 参数解析回过滤器状态
web/src/lib/hooks.ts:401-464。 - 根据可用选项(数据源、文档集、标签)进行校验。
来源:web/src/lib/hooks.ts:340-479
---
大语言模型(LLM)提供商管理
LlmManager 层级结构实现了复杂的大语言模型(LLM)提供商和模型选择逻辑,支持多个覆盖级别。
大语言模型(LLM)选择层级
系统根据严格的优先级顺序确定活跃的大语言模型(LLM)模型:
模型选择优先级
graph TB
Start["确定大语言模型(LLM)模型"]
UserOverride{"用户手动覆盖?<br/>(ChatInputBar)"}
SessionModel{"聊天会话<br/>current_alternate_model?"}
AgentOverride{"角色<br/>llm_model_version_override?"}
UserPref{"用户偏好<br/>default_model?"}
SystemDefault["系统默认<br/>(LLMProviderResponse.default_text)"]
Start --> UserOverride
UserOverride -->|"是"| UseUserOverride["使用用户覆盖"]
UserOverride -->|"否"| SessionModel
SessionModel -->|"是"| UseSessionModel["使用会话模型"]
SessionModel -->|"否"| AgentOverride
AgentOverride -->|"是"| UseAgentOverride["使用角色覆盖"]
AgentOverride -->|"否"| UserPref
UserPref -->|"是"| UseUserPref["使用用户偏好"]
UserPref -->|"否"| SystemDefault
来源:web/src/lib/hooks.ts:509-541, web/src/lib/hooks.ts:697-738, web/src/hooks/useLanguageModels.ts:46-72
钩子特化
Onyx 为大语言模型(LLM)提供商访问提供了不同的钩子:
useLLMProviders(personaId?):面向用户的标准钩子。通过personaId或组成员身份遵循基于角色的访问控制(RBAC)。使用 60 秒的dedupingIntervalweb/src/hooks/useLanguageModels.ts:46-72。useAdminLLMProviders():面向管理员的钩子。获取完整的LLMProviderView对象,包括 ID 和掩码密钥。禁用revalidateIfStale以防止编辑期间 UI 闪烁web/src/hooks/useLanguageModels.ts:95-114。useWellKnownLLMProvider(providerName):预配置钩子。在保存凭证之前,获取内置提供商(OpenAI、Anthropic 等)的模型列表和建议web/src/hooks/useLanguageModels.ts:129-147。
来源:web/src/hooks/useLanguageModels.ts:1-177
---
使用 localStorage 的数据源偏好
useSourcePreferences 钩子管理用户对数据源的偏好,并使用 localStorage 在会话之间持久化。
存储与去重
数据源偏好持久化
graph TB
LocalStorage["localStorage<br/>键: 'selectedInternalSearchSources'"]
Snapshot["SourcePreferencesSnapshot"]
ConfiguredSources["configuredSources<br/>(按 uniqueKey 去重)"]
SelectedSources["selectedSources 状态"]
LocalStorage -->|"挂载时加载"| Snapshot
Snapshot -->|"校验并合并"| SelectedSources
SelectedSources -->|"toggleSource()"| Update["状态更新"]
Update -->|"persistSourcePreferencesState()"| LocalStorage
来源:web/src/lib/hooks.ts:987-1164
getConfiguredSources 中的去重逻辑确保相同类型的索引源和联邦源被同等对待,方法是从唯一键中去除 federated_ 前缀 web/src/lib/hooks.ts:957-985。
---
工具钩子
useObjectState
一个便捷钩子,用于管理复杂状态对象并支持部分更新,类似于基于类的 setState: web/src/lib/hooks.ts:74-87
useLlmManager
一个中央管理器,用于协调当前模型选择、温度覆盖以及聊天界面中提供商的可用性: web/src/lib/hooks.ts:481-872
来源:web/src/lib/hooks.ts:74-87, web/src/lib/hooks.ts:481-872