Skip to content

A7: URL-path method fallback for raw fetch (closes #79) - #104

Merged
AndresL230 merged 3 commits into
mainfrom
claude/a7-url-path-method-fallback
May 13, 2026
Merged

A7: URL-path method fallback for raw fetch (closes #79)#104
AndresL230 merged 3 commits into
mainfrom
claude/a7-url-path-method-fallback

Conversation

@AndresL230

@AndresL230 AndresL230 commented May 13, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds optional urlPathKey field to fingerprint methods schema (src/scanner/fingerprints/types.ts).
  • Adds lookupByUrlPath(provider, url) to the fingerprint registry — matches longest URL-path key first, falls back to _default.
  • Extends elevenlabs.json with 4 URL-path entries (text-to-speech, speech-to-text, voices, _default).
  • Updates cost-utils.ts estimateLocalMonthlyCost to accept optional url and try URL-path lookup when methodSignature is undefined.
  • Threads call.url through the 3 production callers (scan-results.ts, scan-publishing-handler.ts, compression.ts).
  • Adds in-repo integration test: fetch("https://api.elevenlabs.io/v1/text-to-speech/...") resolves to non-stub cost.

D1 measurement

Detection precision      30.95%  (baseline 30.95%, Δ +0.00pp)
Detection recall         42.62%  (baseline 42.62%, Δ +0.00pp)
Provider attribution     79.59%  (baseline 79.59%, Δ +0.00pp)
Finding precision         7.14%  (baseline  7.14%, Δ +0.00pp)
Finding recall           33.33%  (baseline 33.33%, Δ +0.00pp)

No movement — the D1 corpus does not contain a raw-fetch to ElevenLabs (or any of the URL-path providers added in this PR). The in-repo integration test proves the fix works end-to-end; corpus expansion is a follow-up.

Follow-ups

  • Apply URL-path entries to other commonly raw-fetched providers (OpenAI, Anthropic, Cohere, Stripe REST). Separate issues.
  • The _default perRequestCostUsd is conservative ($0.0001); revisit per provider with real pricing data.

Test plan

  • CI benchmark passes (no metric drops > 1pp from baseline)
  • CI test:scanner passes (8 new a7 PASS lines)
  • Manual: scan a repo with fetch(\"https://api.elevenlabs.io/v1/text-to-speech/...\") — endpoint resolves to elevenlabs with a non-stub cost

Closes #79.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added URL-path based cost estimation fallback, enabling more accurate pricing calculations for raw HTTP API calls when SDK method information is unavailable.
    • Enhanced ElevenLabs API cost estimation with additional endpoint coverage.
  • Tests

    • Added comprehensive test suite for URL-path provider fingerprint lookup and cost estimation integration.

Review Change Stack

AndresL230 and others added 2 commits May 13, 2026 15:55
Wire estimateLocalMonthlyCost to accept an optional URL and try
lookupByUrlPath when the SDK method chain is undefined. Updates all
three production callers (scan-results, scan-publishing-handler,
compression) to pass the call's URL through. Adds an integration test
asserting that a raw fetch to api.elevenlabs.io/v1/text-to-speech/...
now resolves to a non-zero per-request cost via the fingerprint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

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

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ 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 Plus

Run ID: 1d1e2aac-e756-4c51-b287-4db6c609ce95

📥 Commits

Reviewing files that changed from the base of the PR and between fe16e41 and 24ed60e.

📒 Files selected for processing (5)
  • src/intelligence/compression.ts
  • src/scan-results.ts
  • src/scanner/fingerprints/CONTRIBUTING.md
  • src/scanner/fingerprints/registry.ts
  • src/webview/scan-publishing-handler.ts
📝 Walkthrough

Walkthrough

This PR implements URL-path method fingerprint fallback for cost estimation. When provider is known but methodSignature is undefined (raw fetch), the system now matches the request URL against provider-specific URL-path keys to resolve the cost model. This enables accurate cost tracking for raw fetch calls to known API providers like ElevenLabs.

Changes

URL-path cost estimation fallback

Layer / File(s) Summary
Fingerprint schema and registry structure
src/scanner/fingerprints/types.ts, src/scanner/fingerprints/registry.ts
MethodFingerprint.pattern is now optional; new urlPathKey field added for URL-path-based matching. Registry initializes per-provider urlPathIndex mapping urlPathKey values to ordered MethodFingerprint lists during startup, with _default as fallback.
URL-path lookup function
src/scanner/fingerprints/registry.ts
New exported lookupByUrlPath(provider, url) parses URL into pathname+search and returns the first matching fingerprint by longest-key-first ordering, or the _default entry; returns null for unknown providers or malformed URLs.
ElevenLabs URL-path fingerprints
src/scanner/fingerprints/elevenlabs.json
Extend fingerprint manifest with urlPathKey-based method entries for v1/text-to-speech, v1/speech-to-text, v1/voices, and _default fallback, each defining httpMethod, endpoint, costModel, and description.
Cost estimation with URL fallback
src/intelligence/cost-utils.ts
estimateLocalMonthlyCost accepts optional url parameter; attempts lookupMethod via methodSignature first, then falls back to lookupByUrlPath(provider, url) when methodSignature is unavailable, enabling raw fetch cost resolution.
Pass URL to cost estimation
src/intelligence/compression.ts, src/scan-results.ts, src/webview/scan-publishing-handler.ts
All three call sites now extract and pass call.url to estimateLocalMonthlyCost alongside existing parameters, enabling URL-path fallback at cost estimation time.
ElevenLabs test helpers
src/test/fixtures/a7/raw-elevenlabs-fetch.ts
New speak(text) and transcribe(audio) async helpers wrap raw HTTP POST requests to ElevenLabs endpoints, returning ArrayBuffer and parsed JSON respectively for testing raw fetch scenarios.
Test coverage and validation
src/test/a7-url-path-fallback.test.ts, src/test/fingerprint-registry.test.ts
New comprehensive test suite validates lookupByUrlPath correctness, longest-key-first ordering, _default fallback, and integration with estimateLocalMonthlyCost. Existing registry tests updated to accept either pattern or urlPathKey and validate uniqueness of both.
Test runner integration
package.json
Add dist-test/test/a7-url-path-fallback.test.js to test:scanner script command chain.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • recost-dev/extension#87: Both PRs modify cost estimation logic; PR #87 consolidates cost-utils unit tests while this PR extends estimateLocalMonthlyCost to accept and use url for URL-path fallback pricing.

🐰 A URL path doth speak volumes true,
Where methods hide, fingerprints shine through,
From API calls raw, costs emerge bright,
The fallback path guides us to light! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and specifically summarizes the main change: adding URL-path method fallback support for raw fetch requests (A7), addressing issue #79.
Linked Issues check ✅ Passed All coding requirements from #79 are met: URL-path schema support added, lookupByUrlPath function implemented, ElevenLabs fingerprints extended, cost-utils accepts optional URL parameter with fallback logic, and call.url threaded through all production callers.
Out of Scope Changes check ✅ Passed All changes are directly within scope of #79: fingerprint schema updates, registry implementation, ElevenLabs entries, cost estimation logic, test additions, and test fixtures. No unrelated modifications detected.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/a7-url-path-method-fallback

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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/intelligence/compression.ts`:
- Around line 362-367: The issue: provider is chosen as context.providers[0] but
methodSig and url are always pulled from context.apiCalls[0], which can mix
providers; fix by finding the first ApiCall in context.apiCalls whose providerId
(or equivalent provider identifier) matches the selected provider (provider.id
or provider.name) and use that call to derive methodSig and url (fall back to
context.apiCalls[0] only if no matching call exists); update the same selection
logic where callsPerDay or other call-derived values are computed (e.g., the
later block around lines ~373) so all call samples are aligned with the chosen
provider variable (provider) rather than unconditionally using
context.apiCalls[0].

In `@src/scan-results.ts`:
- Line 414: monthlyCost is computed once using
estimateLocalMonthlyCost(provider, callsPerDay, call.methodSignature, call.url)
but provider, url, callsPerDay and call.methodSignature are mutated later when
merging synthetic endpoints, leaving stale costs; after the code that
merges/overwrites provider/url/callsPerDay/methodSignature (the synthetic
endpoint merge block), recompute monthlyCost by calling
estimateLocalMonthlyCost(provider, callsPerDay, call.methodSignature, call.url)
(falling back to 0) and assign that value into the result object/property that
originally used monthlyCost so merged synthetic endpoints have correct costs.

In `@src/scanner/fingerprints/registry.ts`:
- Around line 184-186: The current check in registry.ts that returns an entry
when pathAndQuery.includes(entry.urlPathKey) can misclassify when urlPathKey
appears inside other path tokens; update the matching in the function that
iterates fingerprint entries to be segment-aware: normalize the request path
(trim/normalize trailing slashes, decode, lowercase if applicable), split into
path segments and match entry.urlPathKey against full segments or match using
boundary-aware logic (e.g., ensure urlPathKey matches at segment boundaries or
as a normalized prefix) rather than raw substring includes; reference the code
that performs this check (the block using
pathAndQuery.includes(entry.urlPathKey) in the registry lookup) and replace it
with the boundary-aware matching described.

In `@src/scanner/fingerprints/types.ts`:
- Around line 12-23: The MethodFingerprint type currently allows neither pattern
nor urlPathKey to be set; change its definition in
src/scanner/fingerprints/types.ts to encode the contract as a discriminated
union so each entry must have at least one of the two fields (e.g. a union of a
shape with required pattern and optional urlPathKey and a shape with required
urlPathKey and optional pattern), preserve any other existing optional
properties on MethodFingerprint, and update any call sites or constructors that
create MethodFingerprint objects to satisfy the new union (adjust tests/fixtures
as needed).

In `@src/test/fixtures/a7/raw-elevenlabs-fetch.ts`:
- Around line 2-5: The fetch call that assigns to r posts a JSON body but omits
the Content-Type header; update the request options in the fetch(...) invocation
(the POST to "https://api.elevenlabs.io/v1/text-to-speech/voice-abc/stream") to
include headers: { "Content-Type": "application/json" } so the ElevenLabs API
correctly parses the JSON payload (ensure you add or merge into any existing
headers object in that same fetch call).

In `@src/webview/scan-publishing-handler.ts`:
- Line 476: monthlyCost is computed once using
estimateLocalMonthlyCost(provider, callsPerDay, call.methodSignature, call.url)
but later code mutates the fields (provider, callsPerDay, call.methodSignature,
call.url) during the merge/merge-update logic, leaving a stale monthlyCost;
after the merge/update block that modifies those fields (the code that updates
merged endpoints between the initial assignment and the later merging logic),
recompute monthlyCost by calling estimateLocalMonthlyCost again with the updated
provider, callsPerDay, call.methodSignature and call.url and replace the
original value so merged endpoints reflect the correct synthetic cost.
🪄 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 Plus

Run ID: 0cc6c1a0-4bda-474c-b399-bb8b33493193

📥 Commits

Reviewing files that changed from the base of the PR and between cd4604b and fe16e41.

📒 Files selected for processing (11)
  • package.json
  • src/intelligence/compression.ts
  • src/intelligence/cost-utils.ts
  • src/scan-results.ts
  • src/scanner/fingerprints/elevenlabs.json
  • src/scanner/fingerprints/registry.ts
  • src/scanner/fingerprints/types.ts
  • src/test/a7-url-path-fallback.test.ts
  • src/test/fingerprint-registry.test.ts
  • src/test/fixtures/a7/raw-elevenlabs-fetch.ts
  • src/webview/scan-publishing-handler.ts

Comment thread src/intelligence/compression.ts Outdated
Comment thread src/scan-results.ts
Comment thread src/scanner/fingerprints/registry.ts Outdated
Comment on lines +12 to +23
/**
* SDK method chain pattern, e.g. "chat.completions.create".
* Either `pattern` or `urlPathKey` must be set on every entry.
*/
pattern?: string;
/**
* URL-path substring used by `lookupByUrlPath` when an API call has a known
* provider but no SDK method chain (e.g. raw `fetch(...)`). The matcher tries
* the longest `urlPathKey` first; the special value `"_default"` is a
* provider-wide fallback (A7, issue #79).
*/
urlPathKey?: string;

@coderabbitai coderabbitai Bot May 13, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Encode the pattern/urlPathKey requirement in the type, not only in comments.

MethodFingerprint currently allows entries with neither field set, despite Line 14’s contract. Enforcing this at type level prevents invalid fingerprint records from compiling.

Proposed type-safe shape
-export interface MethodFingerprint {
+type MethodFingerprintKey =
+  | { pattern: string; urlPathKey?: string }
+  | { pattern?: string; urlPathKey: string };
+
+export type MethodFingerprint = MethodFingerprintKey & {
   /**
    * SDK method chain pattern, e.g. "chat.completions.create".
    * Either `pattern` or `urlPathKey` must be set on every entry.
    */
-  pattern?: string;
+  pattern?: string;
   /**
    * URL-path substring used by `lookupByUrlPath` when an API call has a known
    * provider but no SDK method chain (e.g. raw `fetch(...)`). The matcher tries
    * the longest `urlPathKey` first; the special value `"_default"` is a
    * provider-wide fallback (A7, issue `#79`).
    */
   urlPathKey?: string;
   /** HTTP verb: GET | POST | PUT | PATCH | DELETE | SUBSCRIBE | RPC */
   httpMethod: string;
   /** Full URL or URL template for the mapped endpoint */
   endpoint: string;
   costModel: CostModel;
   ...
-}
+};
As per coding guidelines, "`src/**/*.ts`: Use TypeScript in strict mode for extension backend."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/scanner/fingerprints/types.ts` around lines 12 - 23, The
MethodFingerprint type currently allows neither pattern nor urlPathKey to be
set; change its definition in src/scanner/fingerprints/types.ts to encode the
contract as a discriminated union so each entry must have at least one of the
two fields (e.g. a union of a shape with required pattern and optional
urlPathKey and a shape with required urlPathKey and optional pattern), preserve
any other existing optional properties on MethodFingerprint, and update any call
sites or constructors that create MethodFingerprint objects to satisfy the new
union (adjust tests/fixtures as needed).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Skipping. The runtime validator in registry.ts already enforces pattern || urlPathKey at JSON load time, and MethodFingerprint instances are only constructed by JSON load (no in-code constructors). A discriminated union would force type-narrowing at every read of m.pattern (which happens many places) for a constraint already covered at the single entry point. The ergonomic cost exceeds the value of compile-time enforcement here.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@AndresL230, understood — that's a reasonable trade-off. Centralising the invariant at the single JSON-load entry point via the runtime validator avoids scattered type-narrowing across all m.pattern read sites, and the ergonomic cost of a discriminated union would be real given the number of call sites.


✏️ Learnings added
Learnt from: AndresL230
Repo: recost-dev/extension PR: 104
File: src/scanner/fingerprints/types.ts:12-23
Timestamp: 2026-05-13T20:11:35.796Z
Learning: In `src/scanner/fingerprints/types.ts`, `MethodFingerprint` deliberately keeps `pattern` and `urlPathKey` as plain optional fields rather than a discriminated union. The `pattern || urlPathKey` invariant is enforced at the single entry point (JSON load) by the runtime validator in `src/scanner/fingerprints/registry.ts`. All `MethodFingerprint` instances are constructed only via JSON load with no in-code constructors, so compile-time union enforcement was judged to have too high an ergonomic cost (type-narrowing at every `m.pattern` read site) relative to the benefit.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment thread src/test/fixtures/a7/raw-elevenlabs-fetch.ts Outdated
Comment thread src/webview/scan-publishing-handler.ts
… recompute, mixed-provider sample, doc, dead fixture

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@AndresL230
AndresL230 merged commit 1b0d029 into main May 13, 2026
3 checks passed
@AndresL230
AndresL230 deleted the claude/a7-url-path-method-fallback branch May 22, 2026 22:50
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.

[Detection] URL-path → method fallback for raw fetch (A7, replaces #72)

1 participant