Skip to content

feat(composio): provider folder modules + user profile persistence#523

Merged
senamakel merged 6 commits intotinyhumansai:mainfrom
senamakel:feat/user-profile
Apr 13, 2026
Merged

feat(composio): provider folder modules + user profile persistence#523
senamakel merged 6 commits intotinyhumansai:mainfrom
senamakel:feat/user-profile

Conversation

@senamakel
Copy link
Copy Markdown
Member

@senamakel senamakel commented Apr 13, 2026

Summary

  • Restructured Composio providers into folder modules: Each provider (Gmail, Notion) is now a dedicated folder (providers/gmail/, providers/notion/) with mod.rs (struct + trait impl), sync.rs (helpers), and tests.rs — following the project convention for new features living in folder modules.
  • Added user profile persistence bridge (providers/profile.rs): ProviderUserProfile data (display name, email, username, avatar) is now persisted into the local user_profile facet table on OAuth connection and RPC profile fetch. Previously this data was fetched, logged, and discarded.
  • Added profile_conn() getter to MemoryClient (pub(crate)) to expose the SQLite connection for direct profile-facet writes without going through the ingestion queue.

Key design decisions

  • Profile facets use FacetType::Context with confidence 0.95 (provider-sourced data is authoritative, not inferred from conversation)
  • Facet keys follow composio:{toolkit}:{field} convention (e.g. composio:gmail:email)
  • Evidence count accumulates naturally via existing profile_upsert conflict resolution
  • Empty/whitespace-only fields are silently skipped
  • Profile persistence is fire-and-forget (non-fatal on failure)

Files changed (10)

File Change
providers/gmail.rsproviders/gmail/{mod,sync,tests}.rs Folder restructure
providers/notion.rsproviders/notion/{mod,sync,tests}.rs Folder restructure
providers/profile.rs New — profile persistence bridge
providers/mod.rs Added pub mod profile; + wired into on_connection_created
composio/ops.rs get_user_profile now persists as side-effect
memory/store/client.rs Added pub(crate) fn profile_conn()

Test plan

  • cargo check passes clean
  • cargo test --lib -- openhuman::composio::providers — 36 tests pass (including 3 new profile tests)
  • Profile facet mapping verified: correct keys, values, confidence, evidence accumulation
  • Empty/whitespace field filtering verified
  • Pre-push hooks pass (fmt, lint, typecheck, rust check)

Summary by CodeRabbit

  • New Features

    • Provider user profile data (email, username, display name, avatar) is now persisted locally when connecting to provider services.
  • Refactor

    • Improved internal code organization by restructuring helper utilities and tests into dedicated modules for better maintainability.

- Introduced a new `profile` module to handle the persistence of user profile data from various providers into the local `user_profile` facet table.
- Enhanced the `composio_get_user_profile` and `fetch_user_profile` functions to call `persist_provider_profile`, ensuring that profile fields like display name, email, and avatar are stored locally for quick access.
- Added debug logging to track the number of facets written during the persistence process, improving observability of profile updates.
- This change aims to enhance user experience by reducing the need for repeated upstream API calls for frequently accessed profile information.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 13, 2026

Warning

Rate limit exceeded

@senamakel has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 5 minutes and 59 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 5 minutes and 59 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5fcb80ab-69f9-40aa-a07a-598c2842dba1

📥 Commits

Reviewing files that changed from the base of the PR and between 3611438 and 51dcc40.

📒 Files selected for processing (7)
  • src/openhuman/composio/ops.rs
  • src/openhuman/composio/providers/gmail/sync.rs
  • src/openhuman/composio/providers/gmail/tests.rs
  • src/openhuman/composio/providers/mod.rs
  • src/openhuman/composio/providers/profile.rs
  • src/openhuman/memory/store/client.rs
  • src/openhuman/memory/store/unified/profile.rs
📝 Walkthrough

Walkthrough

The PR introduces profile persistence for Composio providers by creating a new profile module that writes fetched provider identity fields into the local user_profile facet table, while refactoring Gmail and Notion provider helpers into dedicated sync modules with extracted test files.

Changes

Cohort / File(s) Summary
Profile persistence layer
src/openhuman/composio/providers/profile.rs, src/openhuman/composio/providers/mod.rs, src/openhuman/composio/ops.rs
New profile persistence module with persist_provider_profile function that upserts provider identity facets (display_name, email, username, avatar_url) into user_profile table with 0.95 confidence. Integrated into provider connection flow and Composio profile fetch operation with debug/info logging.
Gmail provider refactoring
src/openhuman/composio/providers/gmail/mod.rs, src/openhuman/composio/providers/gmail/sync.rs, src/openhuman/composio/providers/gmail/tests.rs
Split helper utilities (now_ms, cursor_to_gmail_after_filter, extract_messages, extract_page_token) into dedicated sync module. Extracted unit tests into separate tests module covering message/token extraction and cursor format parsing across multiple JSON shapes.
Notion provider refactoring
src/openhuman/composio/providers/notion/mod.rs, src/openhuman/composio/providers/notion/sync.rs, src/openhuman/composio/providers/notion/tests.rs
Extracted sync helpers (now_ms, extract_results, extract_notion_cursor, extract_page_title) into dedicated sync module. Moved tests to separate tests module. Relaxed visibility of action constants to pub(crate) for use in tests.
Memory client extension
src/openhuman/memory/store/client.rs
Added profile_conn() method to expose internal SQLite connection for facet persistence operations.

Sequence Diagram(s)

sequenceDiagram
    participant CP as ComposioProvider
    participant API as Composio API
    participant Profile as profile module
    participant DB as user_profile table
    
    CP->>API: fetch ProviderUserProfile
    API-->>CP: ProviderUserProfile
    CP->>Profile: persist_provider_profile(profile)
    Profile->>Profile: extract identity fields<br/>(email, display_name, etc.)
    Profile->>DB: upsert facets<br/>(context facet type)
    DB-->>Profile: facets_written count
    Profile-->>CP: facets_written
    CP->>CP: emit debug log<br/>(toolkit, facets_written)
    CP-->>CP: return RpcOutcome/result
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • graycyrus

🐰 Hopping through profiles with glee,
Facets persist where they ought to be,
Gmail and Notion, now tidy and clean,
Sync helpers refactored—best organized we've seen!
Identity fields stored with care—
Profile persistence everywhere!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately captures the two main changes: restructuring provider folders into modules and adding user profile persistence from Composio providers.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@senamakel
Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 13, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
src/openhuman/composio/providers/gmail/tests.rs (1)

42-50: Strengthen the epoch-millis test with an exact expected date.

At Lines 46-50, checking only for '/' can pass even with an incorrect conversion result.

✅ Proposed test hardening
 #[test]
 fn cursor_to_filter_from_epoch_millis() {
-    // 2026-04-01 00:00:00 UTC in millis
-    let millis = "1774915200000";
-    let filter = cursor_to_gmail_after_filter(millis);
-    assert!(filter.is_some());
-    // Should produce a YYYY/MM/DD date.
-    let f = filter.unwrap();
-    assert!(f.contains('/'), "Expected date with slashes, got {f}");
+    // 1970-01-02 00:00:00 UTC in millis
+    assert_eq!(
+        cursor_to_gmail_after_filter("86400000"),
+        Some("1970/01/02".to_string())
+    );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/composio/providers/gmail/tests.rs` around lines 42 - 50, The
test cursor_to_filter_from_epoch_millis currently only checks for a '/' in the
output which is too weak; update the test (and keep using
cursor_to_gmail_after_filter and the same millis "1774915200000") to assert the
exact expected date string (e.g. "2026/04/01") appears in the filter (or equals
it if the function returns only the date) instead of just asserting
f.contains('/'), so replace the loose assertion with a precise equality or
substring check against "2026/04/01".
src/openhuman/composio/providers/profile.rs (1)

82-87: Use debug! for the persistence success log.

This runs on normal fetch/connect paths and reads like development diagnostics, so info! will get noisy quickly.

As per coding guidelines, "Use log/tracing at debug or trace level for development-oriented diagnostics in Rust."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/composio/providers/profile.rs` around lines 82 - 87, The
persistence success log currently uses tracing::info! which is too noisy for
normal fetch/connect flows; change the call to tracing::debug! (keeping the same
structured fields toolkit = %toolkit, facets_written = written and the message
"[composio:profile] persisted provider profile facets") so the message is
emitted at debug level instead of info in the branch that checks written > 0.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/openhuman/composio/providers/gmail/sync.rs`:
- Around line 44-59: The cursor_to_gmail_after_filter function is vulnerable to
leading/trailing whitespace causing parse failures; fix it by trimming the input
at the start (e.g., let s = cursor.trim()) and use that trimmed string for all
subsequent parse attempts (millis parse, NaiveDate::parse_from_str,
DateTime::parse_from_rfc3339) so whitespace does not silently bypass the filter
while preserving the existing return values and formats.

In `@src/openhuman/composio/providers/profile.rs`:
- Around line 58-69: The provider writes use profile::profile_upsert with a
fixed PROVIDER_CONFIDENCE (0.95) for provider-authoritative facets (keys built
from facet_id/key), but the unified store
(src/openhuman/memory/store/unified/profile.rs) only replaces values when new
confidence is greater, so equal-confidence refreshes leave stale values; change
the provider write to use an overwrite path for provider-authoritative facets by
invoking the upsert variant that forces replace-on-equal (or add a parameter to
profile::profile_upsert to allow replace_on_equal) when writing
FacetType::Context for provider data (the code that constructs facet_id/key and
calls profile::profile_upsert), ensuring same-source refreshes replace
equal-confidence values rather than only incrementing evidence_count.

---

Nitpick comments:
In `@src/openhuman/composio/providers/gmail/tests.rs`:
- Around line 42-50: The test cursor_to_filter_from_epoch_millis currently only
checks for a '/' in the output which is too weak; update the test (and keep
using cursor_to_gmail_after_filter and the same millis "1774915200000") to
assert the exact expected date string (e.g. "2026/04/01") appears in the filter
(or equals it if the function returns only the date) instead of just asserting
f.contains('/'), so replace the loose assertion with a precise equality or
substring check against "2026/04/01".

In `@src/openhuman/composio/providers/profile.rs`:
- Around line 82-87: The persistence success log currently uses tracing::info!
which is too noisy for normal fetch/connect flows; change the call to
tracing::debug! (keeping the same structured fields toolkit = %toolkit,
facets_written = written and the message "[composio:profile] persisted provider
profile facets") so the message is emitted at debug level instead of info in the
branch that checks written > 0.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 44e39af3-9e7c-4363-a6e3-27404121f8db

📥 Commits

Reviewing files that changed from the base of the PR and between 3a20599 and 3611438.

📒 Files selected for processing (10)
  • src/openhuman/composio/ops.rs
  • src/openhuman/composio/providers/gmail/mod.rs
  • src/openhuman/composio/providers/gmail/sync.rs
  • src/openhuman/composio/providers/gmail/tests.rs
  • src/openhuman/composio/providers/mod.rs
  • src/openhuman/composio/providers/notion/mod.rs
  • src/openhuman/composio/providers/notion/sync.rs
  • src/openhuman/composio/providers/notion/tests.rs
  • src/openhuman/composio/providers/profile.rs
  • src/openhuman/memory/store/client.rs

Comment thread src/openhuman/composio/providers/gmail/sync.rs
Comment thread src/openhuman/composio/providers/profile.rs
…ion, log level

- Trim whitespace in cursor_to_gmail_after_filter before parsing so
  leading/trailing spaces don't silently bypass the date filter.
- Change profile_upsert condition from `>` to `>=` so equal-confidence
  provider refreshes replace stale values instead of only bumping
  evidence_count.
- Tighten epoch millis test to assert exact date "2026/03/31" instead
  of loose contains('/') check.
- Lower profile persistence log from info to debug to reduce noise in
  normal connect/sync flows.
@senamakel senamakel merged commit ec138c8 into tinyhumansai:main Apr 13, 2026
9 checks passed
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