-
Notifications
You must be signed in to change notification settings - Fork 37
feat(abuse): implement cost reporting for spend-based heuristics #22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -205,26 +205,15 @@ export async function POST(request: NextRequest): Promise<NextResponseType<unkno | |
| ); | ||
| console.debug(`Routing request to ${provider.id}`); | ||
|
|
||
| // Fire-and-forget abuse classification as early as possible | ||
| void classifyAbuse(request, requestBodyParsed, { | ||
| // Start abuse classification early (non-blocking) - we'll await it before creating usage context | ||
| const classifyPromise = classifyAbuse(request, requestBodyParsed, { | ||
| kiloUserId: user.id, | ||
| organizationId, | ||
| projectId, | ||
| provider: provider.id, | ||
| isByok: !!userByok, | ||
| }).then(result => { | ||
| if (result) { | ||
| console.log('Abuse classification result:', { | ||
| verdict: result.verdict, | ||
| risk_score: result.risk_score, | ||
| signals: result.signals, | ||
| identity_key: result.context.identity_key, | ||
| kilo_user_id: user.id, | ||
| requested_model: originalModelIdLowerCased, | ||
| rps: result.context.requests_per_second, | ||
| }); | ||
| } | ||
| }); | ||
|
|
||
| // large responses may run longer than the 800s serverless function timeout, usually this value is set to 8192 tokens | ||
| if (requestBodyParsed.max_tokens && requestBodyParsed.max_tokens > MAX_TOKENS_LIMIT) { | ||
| console.warn(`SECURITY: Max tokens limit exceeded: ${user.id}`, { | ||
|
|
@@ -392,6 +381,28 @@ export async function POST(request: NextRequest): Promise<NextResponseType<unkno | |
|
|
||
| const clonedReponse = response.clone(); // reading from body is side-effectful | ||
|
|
||
| // Await abuse classification (with timeout) to get request_id for cost tracking correlation | ||
| let timeoutId: ReturnType<typeof setTimeout> | undefined; | ||
| const classifyResult = await Promise.race([ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. WARNING: Awaiting abuse classification adds up to 2s latency on the critical path
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jrf0110 i guess this is true? could we fire this after so that we can get request_id eventually (if ever) and then call countAndStoreUsage ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The p99's for the requests to the abuse service are way less than 2s, so in practice, the classify response is going to be resolved already. I didn't want to have to pass in the promise into
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| classifyPromise.finally(() => timeoutId && clearTimeout(timeoutId)), | ||
|
jrf0110 marked this conversation as resolved.
|
||
| new Promise<null>(resolve => { | ||
| timeoutId = setTimeout(() => resolve(null), 2000); | ||
| }), | ||
| ]); | ||
| if (classifyResult) { | ||
| console.log('Abuse classification result:', { | ||
| verdict: classifyResult.verdict, | ||
| risk_score: classifyResult.risk_score, | ||
| signals: classifyResult.signals, | ||
| identity_key: classifyResult.context.identity_key, | ||
| kilo_user_id: user.id, | ||
| requested_model: originalModelIdLowerCased, | ||
| rps: classifyResult.context.requests_per_second, | ||
| request_id: classifyResult.request_id, | ||
| }); | ||
| usageContext.abuse_request_id = classifyResult.request_id; | ||
| } | ||
|
|
||
| accountForMicrodollarUsage(clonedReponse, usageContext, openrouterRequestSpan); | ||
|
|
||
| { | ||
|
|
||


There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SUGGESTION: Misleading comment about when abuse classification is awaited
classifyPromiseis awaited after the upstream response (viaPromise.race([...])), not before creatingusageContext. Updating this comment avoids confusion for future maintainers.