Skip to content

chore(ai): pay#2282

Merged
ehayes2000 merged 1 commit intomainfrom
paywallington
Mar 31, 2026
Merged

chore(ai): pay#2282
ehayes2000 merged 1 commit intomainfrom
paywallington

Conversation

@ehayes2000
Copy link
Copy Markdown
Contributor

No description provided.

@ehayes2000 ehayes2000 requested review from a team as code owners March 30, 2026 20:31
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 30, 2026

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced error handling for chat creation failures by improving detection and validation of payment status and account usage limits.
    • Improved paywall notification system to provide accurate, timely feedback when users encounter payment-related restrictions during chat operations.

Walkthrough

Payment-error handling was added to the chat creation flow: client JS now classifies payment errors and triggers the paywall UI, core util logic uses structured payment-error detection, the Rust router signature now accepts a ChatModelAccess extractor, and related Rust create_chat tests were removed.

Changes

Cohort / File(s) Summary
JS: Chat input component
js/app/packages/app/component/SoupChatInput.tsx
Imported isPaymentError, usePaywallState, and PaywallKey; on createChat error, show paywall (PaywallKey.CHAT_LIMIT) when isPaymentError is true.
JS: Chat creation util
js/app/packages/core/util/create.ts
Replaced brittle string-match for payment failures with isPaymentError(maybeChat) and trigger paywall (PaywallKey.CHAT_LIMIT) when appropriate.
Rust: HTTP router
rust/cloud-storage/chat/src/inbound/http/router.rs
Added ChatModelAccess extractor parameter to create_chat_handler signature (parameter is unused in the handler body).
Rust: create_chat tests removed
rust/cloud-storage/chat/src/inbound/test.rs
Deleted mock_create_router/error_create_router helpers and the entire create_chat test suite (success, default-name, repo error, unauthenticated cases); other tests remain.

Possibly related PRs

  • feat(ai): paywall models #2234: Adds model-access/paywall-related changes that align with this PR’s addition of ChatModelAccess and payment-error/paywall handling.
🚥 Pre-merge checks | ❌ 2

❌ Failed checks (2 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is vague and does not clearly summarize the changeset; 'pay' is too abbreviated and lacks meaningful context about the actual changes. Expand the title to clearly describe the change, such as 'chore(paywall): integrate payment error detection for chat creation' to convey the actual intent and scope.
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to assess whether it relates to the changeset. Add a description explaining the purpose of these changes, such as how the paywall integration improves payment error handling in chat creation.

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


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

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 30, 2026

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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
rust/cloud-storage/chat/src/inbound/http/router.rs (2)

160-166: ⚠️ Potential issue | 🟠 Major

Add err to tracing instrumentation and skip _model_access parameter.

create_chat_handler returns Result and should include the err attribute in its tracing::instrument. The _model_access extractor parameter should also be skipped from spans.

Suggested fix
-#[tracing::instrument(skip(state, user, req), fields(user_id = %user.macro_user_id))]
+#[tracing::instrument(skip(state, user, _model_access, req), err, fields(user_id = %user.macro_user_id))]
 pub async fn create_chat_handler<S: ChatService, Svc: EntityAccessService>(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/cloud-storage/chat/src/inbound/http/router.rs` around lines 160 - 166,
The tracing span for create_chat_handler is missing the err attribute and isn't
skipping the `_model_access` extractor; update the #[tracing::instrument(...)]
on create_chat_handler to include err and include `_model_access` in the skip
list (so skip state, user, _model_access, req) while keeping fields(user_id =
%user.macro_user_id) so errors from the Result-returning function are recorded
and the extractor is not captured in the span.

148-157: ⚠️ Potential issue | 🟠 Major

Add missing 402 response to OpenAPI and fix tracing instrumentation.

The ChatModelAccess extractor returns StatusCode::PAYMENT_REQUIRED (402) when users lack AI model permissions, but this status is not documented in the @utoipa::path macro. Additionally, the create_chat_handler returns Result but the tracing::instrument attribute is missing the err flag, and _model_access should be skipped to prevent logging extraction internals.

Suggested fixes
 #[utoipa::path(
     post,
     path = "/chats",
     tag = "chats",
     operation_id = "create_chat",
     responses(
         (status = 200, body = StringIDResponse),
         (status = 401, body = String),
+        (status = 402, body = String),
         (status = 500, body = String),
     )
 )]
 /// Create a new chat.
-#[tracing::instrument(skip(state, user, req), fields(user_id = %user.macro_user_id))]
+#[tracing::instrument(skip(state, user, req, _model_access), err, fields(user_id = %user.macro_user_id))]
 pub async fn create_chat_handler<S: ChatService, Svc: EntityAccessService>(
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/cloud-storage/chat/src/inbound/http/router.rs` around lines 148 - 157,
Update the utoipa::path macro for the POST /chats endpoint to include a (status
= 402, body = String) response to document ChatModelAccess returning
StatusCode::PAYMENT_REQUIRED, and adjust the tracing instrumentation on
create_chat_handler: add err to the tracing::instrument attribute and add
skip(_model_access) so the extractor internals are not logged; locate the macro
near the create_chat_handler function and the ChatModelAccess extractor to apply
these changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@js/app/packages/app/component/SoupChatInput.tsx`:
- Around line 74-79: SoupChatInput currently calls
cognitionApiServiceClient.createChat directly and duplicates payment-error
handling; move that network call into the queries layer as a TanStack Query
mutation (e.g., add a createChat mutation and exported hook like
useCreateChatMutation in the queries package) and have the central createChat
handler in core/util/create.ts perform payment-error logic. In SoupChatInput
replace the direct call to cognitionApiServiceClient.createChat and local
usePaywallState handling with the new mutation hook
(useCreateChatMutation().mutateAsync or mutate) and consume errors/results from
that hook, removing duplicate payment handling in the component so all
service-client network calls and payment-error behaviors are centralized.

---

Outside diff comments:
In `@rust/cloud-storage/chat/src/inbound/http/router.rs`:
- Around line 160-166: The tracing span for create_chat_handler is missing the
err attribute and isn't skipping the `_model_access` extractor; update the
#[tracing::instrument(...)] on create_chat_handler to include err and include
`_model_access` in the skip list (so skip state, user, _model_access, req) while
keeping fields(user_id = %user.macro_user_id) so errors from the
Result-returning function are recorded and the extractor is not captured in the
span.
- Around line 148-157: Update the utoipa::path macro for the POST /chats
endpoint to include a (status = 402, body = String) response to document
ChatModelAccess returning StatusCode::PAYMENT_REQUIRED, and adjust the tracing
instrumentation on create_chat_handler: add err to the tracing::instrument
attribute and add skip(_model_access) so the extractor internals are not logged;
locate the macro near the create_chat_handler function and the ChatModelAccess
extractor to apply these changes.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 630fe19a-252f-4649-a760-cdc6fe96dff3

📥 Commits

Reviewing files that changed from the base of the PR and between 963c554 and 1c07899.

📒 Files selected for processing (3)
  • js/app/packages/app/component/SoupChatInput.tsx
  • js/app/packages/core/util/create.ts
  • rust/cloud-storage/chat/src/inbound/http/router.rs

Comment on lines 74 to +79
const response = await cognitionApiServiceClient.createChat({});
if (isErr(response)) {
if (isPaymentError(response)) {
const { showPaywall } = usePaywallState();
showPaywall(PaywallKey.CHAT_LIMIT);
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Move chat creation to a query-layer mutation; avoid direct client calls in component.

This component still calls cognitionApiServiceClient.createChat directly and now duplicates payment-error handling already present in js/app/packages/core/util/create.ts (Line 239-254). Centralize this in the TanStack Query layer and consume that mutation here to prevent logic drift.

As per coding guidelines, "All network calls to service clients MUST go through TanStack Query in the queries package. Do NOT call service clients directly from components or other packages."

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

In `@js/app/packages/app/component/SoupChatInput.tsx` around lines 74 - 79,
SoupChatInput currently calls cognitionApiServiceClient.createChat directly and
duplicates payment-error handling; move that network call into the queries layer
as a TanStack Query mutation (e.g., add a createChat mutation and exported hook
like useCreateChatMutation in the queries package) and have the central
createChat handler in core/util/create.ts perform payment-error logic. In
SoupChatInput replace the direct call to cognitionApiServiceClient.createChat
and local usePaywallState handling with the new mutation hook
(useCreateChatMutation().mutateAsync or mutate) and consume errors/results from
that hook, removing duplicate payment handling in the component so all
service-client network calls and payment-error behaviors are centralized.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
js/app/packages/app/component/SoupChatInput.tsx (1)

93-103: ⚠️ Potential issue | 🟠 Major

Background send also calls service client directly.

cognitionApiServiceClient.sendStreamChatMessage is called directly in the component. This should also be moved to the queries layer. Consider creating a mutation or using a centralized utility that wraps this call.

As per coding guidelines, "All network calls to service clients MUST go through TanStack Query in the queries package. Do NOT call service clients directly from components or other packages."

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

In `@js/app/packages/app/component/SoupChatInput.tsx` around lines 93 - 103, The
component is calling cognitionApiServiceClient.sendStreamChatMessage directly
(inside the backgroundSend branch) which violates the rule that all
service-client network calls must go through TanStack Query in the queries
package; create a mutation (e.g., sendStreamChatMessageMutation) inside the
queries package that wraps cognitionApiServiceClient.sendStreamChatMessage,
export hooks (useSendStreamChatMessage or similar), then in SoupChatInput
replace the direct call with the mutation's mutate/mutateAsync call (pass
content, model, chat_id, attachments, toolset) and move the invalidateAllSoup
call to run after the mutation succeeds (or in onSettled/onSuccess) to preserve
behavior and error handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@rust/cloud-storage/chat/src/inbound/http/router.rs`:
- Around line 164-165: The OpenAPI doc for create_chat is missing the 402
response emitted by the ChatModelAccess extractor; update the #[utoipa::path]
attribute on the create_chat handler to include a response with status = 402
(e.g., description "Payment required / model access denied") so the spec matches
the actual behavior of ChatModelAccess rejecting unauthorized model access.
- Around line 164-165: Add a new unit test named
create_chat_payment_required_returns_402 in
rust/cloud-storage/chat/src/inbound/test.rs that exercises the create_chat HTTP
route and asserts a 402 PAYMENT_REQUIRED is returned when the ChatModelAccess
extractor rejects; specifically, invoke the create_chat handler (the same router
used in existing tests) with request inputs that simulate missing model
permissions so ChatModelAccess fails, then assert the response status is 402.
Ensure the test uses the same route construction/fixtures as other inbound tests
so the extractor runs (referencing create_chat and ChatModelAccess) and includes
any necessary headers/body used by create_chat to reach the extractor path.

---

Outside diff comments:
In `@js/app/packages/app/component/SoupChatInput.tsx`:
- Around line 93-103: The component is calling
cognitionApiServiceClient.sendStreamChatMessage directly (inside the
backgroundSend branch) which violates the rule that all service-client network
calls must go through TanStack Query in the queries package; create a mutation
(e.g., sendStreamChatMessageMutation) inside the queries package that wraps
cognitionApiServiceClient.sendStreamChatMessage, export hooks
(useSendStreamChatMessage or similar), then in SoupChatInput replace the direct
call with the mutation's mutate/mutateAsync call (pass content, model, chat_id,
attachments, toolset) and move the invalidateAllSoup call to run after the
mutation succeeds (or in onSettled/onSuccess) to preserve behavior and error
handling.
🪄 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 86a242c9-ddf8-44b8-b622-d5f63191e557

📥 Commits

Reviewing files that changed from the base of the PR and between 1c07899 and 3386727.

📒 Files selected for processing (4)
  • js/app/packages/app/component/SoupChatInput.tsx
  • js/app/packages/core/util/create.ts
  • rust/cloud-storage/chat/src/inbound/http/router.rs
  • rust/cloud-storage/chat/src/inbound/test.rs
💤 Files with no reviewable changes (1)
  • rust/cloud-storage/chat/src/inbound/test.rs

Comment on lines +164 to +165
// 402 on no perms
_model_access: ChatModelAccess,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n rust/cloud-storage/chat/src/inbound/http/router.rs | head -180 | tail -40

Repository: macro-inc/macro

Length of output: 1509


🏁 Script executed:

rg -A 20 "fn create_chat" rust/cloud-storage/chat/src/inbound/http/router.rs

Repository: macro-inc/macro

Length of output: 681


🏁 Script executed:

rg -B 5 "ChatModelAccess" rust/cloud-storage/chat/src/inbound/http/router.rs | head -50

Repository: macro-inc/macro

Length of output: 589


🏁 Script executed:

rg -r "impl.*FromRequestParts.*ChatModelAccess|impl.*FromRequest.*ChatModelAccess" --type rust

Repository: macro-inc/macro

Length of output: 455


🏁 Script executed:

fd -type f -name "*.rs" | xargs rg "struct ChatModelAccess" -A 30

Repository: macro-inc/macro

Length of output: 2960


🏁 Script executed:

cat -n rust/cloud-storage/chat/src/inbound/http/extractors.rs | grep -A 50 "NoModelAccess =>"

Repository: macro-inc/macro

Length of output: 1522


Document the 402 response for create_chat in the OpenAPI specification.

The ChatModelAccess extractor on line 165 rejects with 402 (PAYMENT_REQUIRED) when users lack model permissions, but the #[utoipa::path] macro for create_chat currently omits this status code from its responses. Add the missing 402 response to match the actual handler behavior:

#[utoipa::path(
    post,
    path = "/chats",
    tag = "chats",
    operation_id = "create_chat",
    responses(
        (status = 200, body = StringIDResponse),
        (status = 401, body = String),
+       (status = 402, body = String),
        (status = 500, body = String),
    )
)]
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@rust/cloud-storage/chat/src/inbound/http/router.rs` around lines 164 - 165,
The OpenAPI doc for create_chat is missing the 402 response emitted by the
ChatModelAccess extractor; update the #[utoipa::path] attribute on the
create_chat handler to include a response with status = 402 (e.g., description
"Payment required / model access denied") so the spec matches the actual
behavior of ChatModelAccess rejecting unauthorized model access.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Verify that create_chat tests exist and include paywall/402 coverage.
fd -a test.rs rust/cloud-storage/chat/src/inbound
rg -n -C3 --type=rust 'create_chat|PAYMENT_REQUIRED|status\s*[:=]\s*402|NoModelAccess|AI features require a paid subscription' rust/cloud-storage/chat/src/inbound/test.rs

Repository: macro-inc/macro

Length of output: 121


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Examining router.rs context around lines 164-165 ==="
sed -n '155,175p' rust/cloud-storage/chat/src/inbound/http/router.rs

echo -e "\n=== Checking test.rs file size and structure ==="
wc -l rust/cloud-storage/chat/src/inbound/test.rs
head -50 rust/cloud-storage/chat/src/inbound/test.rs

echo -e "\n=== Search for 402 or PAYMENT_REQUIRED in entire chat inbound module ==="
rg -n 'PAYMENT_REQUIRED|402|NoModelAccess' rust/cloud-storage/chat/src/inbound/ --type=rust

echo -e "\n=== Search for create_chat function definition and tests ==="
rg -n 'fn create_chat|create_chat_test|test.*create_chat' rust/cloud-storage/chat/src/ --type=rust

echo -e "\n=== Check for inline tests in router.rs ==="
rg -n '#\[cfg.*test' rust/cloud-storage/chat/src/inbound/http/router.rs --type=rust

Repository: macro-inc/macro

Length of output: 4246


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Full test.rs file to see existing tests ==="
cat rust/cloud-storage/chat/src/inbound/test.rs

echo -e "\n=== Verify ChatModelAccess is an extractor (FromRequestParts) ==="
rg -n 'impl.*FromRequestParts.*ChatModelAccess|struct ChatModelAccess' rust/cloud-storage/chat/src/inbound/http/extractors.rs -A5

Repository: macro-inc/macro

Length of output: 15631


Add a test for the 402 rejection path in create_chat.

The ChatModelAccess extractor properly rejects with PAYMENT_REQUIRED (402) when model access is unavailable, but this path lacks HTTP-level test coverage. Add a create_chat_payment_required_returns_402 test in rust/cloud-storage/chat/src/inbound/test.rs that verifies the endpoint returns 402 when the extractor rejects due to missing model permissions.

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

In `@rust/cloud-storage/chat/src/inbound/http/router.rs` around lines 164 - 165,
Add a new unit test named create_chat_payment_required_returns_402 in
rust/cloud-storage/chat/src/inbound/test.rs that exercises the create_chat HTTP
route and asserts a 402 PAYMENT_REQUIRED is returned when the ChatModelAccess
extractor rejects; specifically, invoke the create_chat handler (the same router
used in existing tests) with request inputs that simulate missing model
permissions so ChatModelAccess fails, then assert the response status is 402.
Ensure the test uses the same route construction/fixtures as other inbound tests
so the extractor runs (referencing create_chat and ChatModelAccess) and includes
any necessary headers/body used by create_chat to reach the extractor path.

@ehayes2000 ehayes2000 merged commit ed6061a into main Mar 31, 2026
38 of 39 checks passed
@ehayes2000 ehayes2000 deleted the paywallington branch March 31, 2026 13:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant