Skip to content

Forward idempotency_key on AI assist so retries are not charged twice - #110

Merged
feruzm merged 2 commits into
ecency:mainfrom
hiveuprss:cursor/ai-assist-idempotency-key-24e9
Sep 30, 2026
Merged

feruzm merged 2 commits into
ecency:mainfrom
hiveuprss:cursor/ai-assist-idempotency-key-24e9

Conversation

@hiveuprss

Copy link
Copy Markdown
Contributor

ePoints dedupes a repeated ai-assist request only when it sees the same idempotency_key. The proxy was dropping the key the SDK already sends.

ePoints dedupes a repeated ai-assist request only when it sees the same
idempotency_key. The proxy was dropping the key the SDK already sends.

Co-authored-by: hivetrending <hiveuprss@users.noreply.github.com>
@qodo-code-review

Copy link
Copy Markdown

ⓘ Qodo reviews are paused because your trial has ended. Ask your workspace admin to add credits to resume reviews. Manage billing

@coderabbitai

coderabbitai Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: 2b5889d3-4b73-486f-a4c1-16e16008f24d


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Forward AI assist idempotency keys to prevent duplicate charges

🐞 Bug fix 🧪 Tests 🕐 10-20 Minutes

Grey Divider

AI Description

• Forward client-supplied AI assist idempotency keys so ePoints can deduplicate retries.
• Preserve requests from older clients that omit the key.
• Add handler tests for both request forms without calling the upstream service.
Diagram

sequenceDiagram
    actor Client
    participant Proxy as AI assist proxy
    participant Validator as Code validator
    participant EPoints
    Client->>Proxy: Request with optional key
    Proxy->>Validator: Validate signed code
    Validator-->>Proxy: Username
    Proxy->>EPoints: POST user and optional key
    EPoints-->>Proxy: Result or cached retry
    Proxy-->>Client: Assist response
Loading
High-Level Assessment

Reusing the handler’s existing copy-if-present helper is the narrowest fix: it forwards the key without changing older clients’ payloads or moving validation away from ePoints. The replaceable validation and upstream calls enable isolated handler tests.

Files changed (2) +92 / -3

Bug fix (1) +17 / -3
PrivateApi.Misc.csPreserve optional idempotency keys in AI assist requests +17/-3

Preserve optional idempotency keys in AI assist requests

• Copies a client-supplied idempotency_key into the ePoints payload alongside action and text, while omitting it when absent. Introduces replaceable AI assist validation and upstream delegates for handler tests without changing the production call or timeout.

dotnet/EcencyApi/Handlers/PrivateApi.Misc.cs

Tests (1) +75 / -0
AiAssistHandlerTests.csTest AI assist forwarding with and without an idempotency key +75/-0

Test AI assist forwarding with and without an idempotency key

• Adds handler tests that inspect the recorded upstream payload and verify the validated username, request fields, and optional key. Test-specific validation and upstream delegates avoid external calls and are restored after each test.

dotnet/EcencyApi.Tests/AiAssistHandlerTests.cs

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Retry behavior is absent from parity docs ✓ Resolved
Description
AiAssist now forwards idempotency_key to the upstream service, but the parity harness has no
entry for /private-api/ai-assist. When a client repeats a request with the same key, the upstream
returns a cached result instead of processing another paid assist, and the new forwarding test does
not document that behavior relative to the reference image.
Code

dotnet/EcencyApi/Handlers/PrivateApi.Misc.cs[627]

+        MiscCopyIfPresent(data, body, "action", "text", "idempotency_key");
Evidence
The changed handler forwards the key, and the new test confirms it reaches the upstream payload. The
parity harness's divergence list has no AI assist entry.

Rule 2667942: Require tests and parity divergence docs for observable endpoint behavior changes
dotnet/EcencyApi/Handlers/PrivateApi.Misc.cs[624-629]
dotnet/EcencyApi.Tests/AiAssistHandlerTests.cs[37-54]
dotnet/parity/driver.py[273-344]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
AI assist now forwards the retry key, but its changed behavior has no parity entry.
## Fix Focus Areas
- dotnet/EcencyApi/Handlers/PrivateApi.Misc.cs[624-629]
- dotnet/parity/driver.py[273-344]
## Recommended Fix
Add an AI assist entry under `dotnet/parity/` describing the reference image's behavior and the behavior of keyed retries. Keep the existing forwarding test.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. The test validator uses strict parsing ✓ Resolved
Description
The injected AiAssistValidateCode test delegate reads the client-supplied code with GetValue()
rather than JsVal.TryGetStringLenient(). A test request containing a lone-surrogate escape would
throw in the delegate before it could exercise the handler's forwarding path, unlike the production
validator.
Code

dotnet/EcencyApi.Tests/AiAssistHandlerTests.cs[23]

+            var code = body["code"]?.GetValue<string>();
Evidence
The new delegate receives the request body and calls the strict string accessor. The existing
lenient helper documents and handles the lone-surrogate case that can make that accessor throw.

Rule 2667903: Use lenient JSON helpers for client/upstream string extraction and serialization
dotnet/EcencyApi.Tests/AiAssistHandlerTests.cs[20-25]
dotnet/EcencyApi/Infrastructure/JsVal.cs[44-73]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The AI assist test validator extracts a client-supplied JSON string with a strict accessor that can throw on lone-surrogate escapes.
## Fix Focus Areas
- dotnet/EcencyApi.Tests/AiAssistHandlerTests.cs[20-25]
## Recommended Fix
Check that `body["code"]` is a `JsonValue`, then use `JsVal.TryGetStringLenient()` to obtain the code before applying the test validator's prefix check.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can route each severity your way: inline, summary, both, or drop

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread dotnet/EcencyApi/Handlers/PrivateApi.Misc.cs
Comment thread dotnet/EcencyApi.Tests/AiAssistHandlerTests.cs Outdated
The test validator used GetValue<string>(), which throws on a lone-surrogate
escape that production ValidateCode accepts via JsVal.TryGetStringLenient.
Note in the parity driver why keyed-retry forwarding stays out of
KNOWN_DIVERGENCES: the catalog never sends a valid code, so those cases still
match the reference image, and the unit test covers the key.

Co-authored-by: hivetrending <hiveuprss@users.noreply.github.com>
@feruzm
feruzm merged commit df48fb9 into ecency:main Sep 30, 2026
2 checks passed
@feruzm

feruzm commented Sep 30, 2026

Copy link
Copy Markdown
Member

@hiveuprss thank you! 👍

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.

3 participants