Skip to content

Comments

Replace RPC string dispatch with direct service trait calls in GraphQL#216

Merged
penso merged 6 commits intomainfrom
claude/refactor-graphql-rpc-RjkWZ
Feb 23, 2026
Merged

Replace RPC string dispatch with direct service trait calls in GraphQL#216
penso merged 6 commits intomainfrom
claude/refactor-graphql-rpc-RjkWZ

Conversation

@penso
Copy link
Collaborator

@penso penso commented Feb 23, 2026

Summary

This PR refactors the GraphQL resolver layer to call domain services directly through the Services trait bundle instead of using string-based RPC dispatch. This eliminates the ServiceCaller indirection and aligns GraphQL with the same code path used by the RPC layer.

Key Changes

  • Removed RPC indirection: Replaced rpc_call! and rpc_json_call! macros with direct service method calls via services!(ctx) helper
  • New SystemInfoService trait: Extracted gateway-level system information methods (health, status, presence, nodes, heartbeat) into a dedicated service trait, replacing ad-hoc RPC dispatch
  • Updated resolver implementations: All query and mutation resolvers now call service methods directly with proper error mapping via from_service() and from_service_json() helpers
  • Refactored test mocks: Replaced single MockCaller with a central MockDispatch and individual mock service structs implementing each service trait, preserving test compatibility
  • Updated context and schema: Modified GraphQLContext to hold Services bundle directly instead of ServiceCaller, and updated schema builder to inject services
  • Gateway integration: Created GatewaySystemInfoService to provide gateway-internal state to the GraphQL layer without exposing internals

Implementation Details

  • The Services bundle from moltis-service-traits is now injected into GraphQL context and used by all resolvers
  • Gateway-level operations (connections, nodes, hooks) are handled by GatewaySystemInfoService which reads from GatewayState
  • Domain service operations delegate to the appropriate service trait methods with the same parameters
  • Error handling is consistent: service errors are converted to GraphQL errors via from_service() helpers
  • Test compatibility is maintained by mapping old RPC method names to service trait method calls in mock implementations
  • Stub implementations (NoopSkillsStub, NoopSystemInfoService) provide sensible defaults for services not configured in all contexts

This change improves type safety, reduces string-based dispatch overhead, and makes the code path consistent between GraphQL and RPC layers.

https://claude.ai/code/session_01Kof5A1V5HD5eHjs6Q8kdGb

GraphQL resolvers now call service methods directly via an `Arc<Services>`
bundle instead of going through the string-based `ServiceCaller` dispatch.
This eliminates the `GatewayServiceCaller` (~350-line match block) and the
`rpc_call!`/`rpc_json_call!` macros, making both GraphQL and RPC share
the exact same service trait objects with zero indirection.

Key changes:
- Add `Services` bundle struct and `SystemInfoService` trait to
  moltis-service-traits for shared service access
- Replace `ServiceCaller` trait with direct `Arc<Services>` in GqlContext
- Rewrite all query/mutation resolvers to call services directly
- Add `GatewaySystemInfoService` for gateway-state queries (health,
  status, presence, nodes, hooks, heartbeat)
- Add `GatewayServices::to_services()` to bridge gateway into the
  shared Services bundle
- Update integration tests with per-trait mock implementations

https://claude.ai/code/session_01Kof5A1V5HD5eHjs6Q8kdGb
@codspeed-hq
Copy link
Contributor

codspeed-hq bot commented Feb 23, 2026

Merging this PR will improve performance by 76.43%

⚡ 1 improved benchmark
✅ 38 untouched benchmarks
⏩ 5 skipped benchmarks1

Performance Changes

Benchmark BASE HEAD Efficiency
env_substitution 18.4 µs 10.4 µs +76.43%

Comparing claude/refactor-graphql-rpc-RjkWZ (a2fef5b) with main (58b3148)

Open in CodSpeed

Footnotes

  1. 5 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

Break the 6092-line methods.rs monolith into focused submodules:
- methods/mod.rs: types, authorization, MethodRegistry, tests
- methods/gateway.rs: gateway-internal handlers (health, status, presence, heartbeat)
- methods/node.rs: node handlers (list, describe, rename, invoke, events)
- methods/pairing.rs: pairing handlers (pair request/approve/reject, device tokens)
- methods/services.rs: service-delegating handlers (agent, sessions, chat, channels,
  config, cron, heartbeat, models, providers, skills, mcp, voice, memory, hooks)
- methods/voice.rs: voice provider detection, ElevenLabs catalog, provider settings

Also applies rustfmt to graphql crate files.

https://claude.ai/code/session_01Kof5A1V5HD5eHjs6Q8kdGb
@penso penso force-pushed the claude/refactor-graphql-rpc-RjkWZ branch from 316c8a2 to 413419c Compare February 23, 2026 05:41
The test was racing with kimi_headers_contain_required_fields on the
shared ~/.config/moltis/kimi_device_id file, causing intermittent CI
failures. Extract path-parameterized helper and use tempdir in tests.
@penso penso force-pushed the claude/refactor-graphql-rpc-RjkWZ branch from 413419c to 1f47fe3 Compare February 23, 2026 05:42
filter({ hasText: "OpenAI" }) is a substring match that also picks up
"OpenAI Codex" (OAuth), causing tests to click the wrong provider.
Use exact regex/text matching on the provider name element instead.

Also make moveToVoiceStep return false on timeout instead of crashing,
so the voice badge test skips gracefully when the step is unreachable.
…ql-rpc-RjkWZ

# Conflicts:
#	crates/web/ui/e2e/specs/onboarding.spec.js
#	crates/web/ui/e2e/specs/providers.spec.js
…ql-rpc-RjkWZ

# Conflicts:
#	crates/gateway/src/graphql_routes.rs
@penso penso merged commit 8258104 into main Feb 23, 2026
24 checks passed
@penso penso deleted the claude/refactor-graphql-rpc-RjkWZ branch February 23, 2026 16:53
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.

2 participants