Skip to content

refactor: unify actor stores into IGAgentActorStore#148

Open
eanzhao wants to merge 11 commits intodevfrom
fix/agent-store
Open

refactor: unify actor stores into IGAgentActorStore#148
eanzhao wants to merge 11 commits intodevfrom
fix/agent-store

Conversation

@eanzhao
Copy link
Copy Markdown
Contributor

@eanzhao eanzhao commented Apr 8, 2026

Summary

  • 删除 NyxIdChatActorStoreStreamingProxyActorStore(进程内 ConcurrentDictionary 事实源),统一收敛到已有的 IGAgentActorStore / ChronoStorageGAgentActorStore(chrono-storage 持久化)
  • 消除"一个 agent 类型一个 store"的架构违规,遵循中间层状态约束:禁止中间层维护 ID→事实状态的进程内映射
  • GAgentTypeName 从硬编码字符串改为 typeof(T).FullName!,获得编译期类型安全

Changes

NyxIdChat:

  • 删除 NyxIdChatActorStore.cs(85 行 ConcurrentDictionary + lock 的进程内 store)
  • 所有 CRUD 端点切换到 IGAgentActorStore,添加 chrono-storage 不可用时的容错降级
  • NyxIdChatServiceDefaults.GAgentTypeNametypeof(NyxIdChatGAgent).FullName!

StreamingProxy:

  • 删除 StreamingProxyActorStore.cs(103 行双 ConcurrentDictionary 的进程内 store)
  • Room CRUD 端点切换到 IGAgentActorStore
  • HandleJoinAsync 移除冗余的 store.AddParticipant()(actor 已通过事件追踪 participants)
  • HandleListParticipantsAsync 暂返回空列表,标记 TODO 需 readmodel(actor state 已持有数据,但直读违反读写分离)
  • 添加 Aevatar.Studio.Application 项目引用
  • 新增 StreamingProxyDefaults.GAgentTypeName

Not in scope

  • ListParticipants readmodel 建设(后续通过 projection pipeline 实现)
  • ListRooms 返回 roomName(需 readmodel 支持)

Test plan

  • dotnet build aevatar.slnx — 0 errors
  • 验证 NyxIdChat conversation CRUD 端点正常工作
  • 验证 StreamingProxy room CRUD 端点正常工作
  • 验证 NyxID relay webhook 正常路由消息

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 (1M context) noreply@anthropic.com

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 440404abda

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +73 to +75
await actorStore.AddActorAsync(StreamingProxyDefaults.GAgentTypeName, roomId, ct);
}
catch (InvalidOperationException)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Handle chrono-storage failures in room creation

HandleCreateRoomAsync only catches InvalidOperationException, but IGAgentActorStore.AddActorAsync can also fail with ChronoStorageServiceException (timeouts/403/network). In that case the exception escapes after the room actor has already been created and initialized, so the API returns 500 while leaving an orphan room actor that is not persisted in the actor store and therefore not discoverable by list APIs.

Useful? React with 👍 / 👎.

{
var participants = store.ListParticipants(scopeId, roomId);
return Task.FromResult<IResult>(Results.Ok(participants));
return Task.FromResult<IResult>(Results.Ok(Array.Empty<object>()));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Return persisted participants instead of empty list

HandleListParticipantsAsync now unconditionally returns an empty array, so after POST /participants succeeds there is no way to retrieve current room membership via the query endpoint. This is a functional regression for any client that relies on snapshot reads (e.g., reconnect/refresh flows) rather than having an active SSE stream at all times.

Useful? React with 👍 / 👎.

…oints

- Replace generic InvalidOperationException catch blocks with specific handling for OperationCanceledException and logging for other exceptions
- Add ILoggerFactory dependency injection and structured logging for actor store operations
- Update TODO comment to clarify participant snapshot query requirements
- Remove unused Microsoft.Extensions.DependencyInjection.Extensions namespace import
eanzhao and others added 9 commits April 8, 2026 17:03
…ntation

- Define IStreamingProxyParticipantStore interface in Aevatar.Studio.Application
- Implement ChronoStorageStreamingProxyParticipantStore (persistent, survives restarts)
- Register in DI as singleton
- Endpoints not yet wired (will be connected when feature/agents-group merges)

Prepares for issue #143 blocker #2: eliminates ConcurrentDictionary participant
tracking when combined with feature/agents-group branch.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace chrono-storage implementations with event-sourced GAgent actors for all core stores (user config, chat history, connector/role catalogs, etc.). Each store now has a corresponding GAgent that publishes state snapshots after every change, enabling readmodel subscribers to stay up-to-date without reading write-model state.

- Add GAgent projects with protobuf message definitions for each store
- Implement actor-backed store implementations that send commands to GAgents
- Update dependency injection to wire actor-backed stores
- Add TODO comment about handling corrupt-data exceptions in streaming proxy participant store
- Add new readmodel GAgents (UserMemoryReadModelGAgent, RoleCatalogReadModelGAgent, etc.) that receive state updates via SendToAsync
- Modify write GAgents to push state snapshots to readmodel counterparts instead of publishing snapshots
- Remove chrono-storage implementations (ChronoStorageScriptStoragePort, ChronoStorageGAgentActorStore, etc.)
- Update proto messages to include ReadModelUpdateEvent definitions and clarify snapshot semantics
- Refactor ActorBackedStore implementations to use per-request readmodel subscriptions
- Convert singleton GAgents to per-scope where appropriate (UserConfigGAgent, GAgentRegistryGAgent)
- Simplify write-only GAgents (ScriptStorageGAgent, WorkflowStorageGAgent) by removing snapshot publishing
Add runtime checks in PushToReadModelAsync methods to create read model actors if missing
Add scope isolation to storage ports and catalog stores using IAppScopeResolver
Update ActorBackedUserConfigStore to use storage options for default runtime URLs
Add EnsureIndexActorAsync in ChatConversationGAgent to create index actors on demand
Update tests to reflect scope-aware actor IDs
… utilities

Introduce ActorCommandDispatcher and ReadModelSnapshotReader to eliminate duplicated
SendCommandAsync and readmodel subscription logic across all actor-backed stores.
Add AppScopeResolverExtensions for consistent scope ID resolution with default fallback.
This reduces code duplication and centralizes actor interaction patterns.
…models

- Add new projection library (Aevatar.Studio.Projection) for current-state read models
- Replace per-actor read model GAgents with centralized projection system
- Implement UserConfig projection with query port for pure-read operations
- Remove legacy ScriptStorage and WorkflowStorage GAgents and ports
- Clean up obsolete read model events from all GAgent protobuf definitions
- Update dependency injection and solution configuration
- Add Aevatar.Studio.Tests project with initial unit tests for validation, text diff, and authentication components
- Improve backpressure initialization in ForEach, MapReduce, and ParallelFanOut modules to handle null/uninitialized states
- Fix streaming tool executor to correctly yield completed results when tools finish after initial drain
- Update service invocation dispatcher to prioritize identity tenant ID over payload scope for security
- Add Elasticsearch projection store support for recursive well-known types
- Update coverage quality guard to exclude Studio application/infrastructure assemblies
- Fix various test assertions and documentation updates
- Introduce IUserConfigQueryPort for read-only access to projection store
- Introduce IUserConfigCommandService for dispatching commands to actor
- Replace IUserConfigStore usage in endpoints and services with query port
- Update NyxIdUserLlmPreferencesStore to use query port instead of store
- Add projection components registration and document store configuration
- Remove ActorBackedUserConfigStore and related tests
- Update test mocks to use new interfaces
Add entities.json and mempalace.yaml to .gitignore to prevent accidental commits of local configuration files.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant