Skip to content

Commit 2e32a59

Browse files
authored
Merge branch 'RooCodeInc:main' into main
2 parents 759cc2a + eb68c65 commit 2e32a59

File tree

14 files changed

+232
-139
lines changed

14 files changed

+232
-139
lines changed

apps/web-roo-code/src/components/CookieConsentWrapper.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import ReactCookieConsent from "react-cookie-consent"
55
import { Cookie } from "lucide-react"
66
import { getDomain } from "tldts"
77
import { CONSENT_COOKIE_NAME } from "@roo-code/types"
8-
import { dispatchConsentEvent } from "@/lib/analytics/consent-manager"
8+
import { handleConsentAccept, handleConsentReject } from "@/lib/analytics/consent-manager"
99

1010
/**
1111
* GDPR-compliant cookie consent banner component
@@ -23,11 +23,11 @@ export function CookieConsentWrapper() {
2323
}, [])
2424

2525
const handleAccept = () => {
26-
dispatchConsentEvent(true)
26+
handleConsentAccept()
2727
}
2828

2929
const handleDecline = () => {
30-
dispatchConsentEvent(false)
30+
handleConsentReject()
3131
}
3232

3333
const extraCookieOptions = cookieDomain

apps/web-roo-code/src/components/providers/posthog-provider.tsx

Lines changed: 24 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import { usePathname, useSearchParams } from "next/navigation"
44
import posthog from "posthog-js"
55
import { PostHogProvider as OriginalPostHogProvider } from "posthog-js/react"
6-
import { useEffect, Suspense, useState } from "react"
7-
import { hasConsent, onConsentChange } from "@/lib/analytics/consent-manager"
6+
import { useEffect, Suspense } from "react"
7+
import { hasConsent } from "@/lib/analytics/consent-manager"
88

99
function PageViewTracker() {
1010
const pathname = usePathname()
@@ -28,11 +28,9 @@ function PageViewTracker() {
2828
}
2929

3030
export function PostHogProvider({ children }: { children: React.ReactNode }) {
31-
const [isInitialized, setIsInitialized] = useState(false)
32-
3331
useEffect(() => {
34-
// Initialize PostHog only on the client side AND when consent is given
35-
if (typeof window !== "undefined") {
32+
// Initialize PostHog immediately on the client side
33+
if (typeof window !== "undefined" && !posthog.__loaded) {
3634
const posthogKey = process.env.NEXT_PUBLIC_POSTHOG_KEY
3735
const posthogHost = process.env.NEXT_PUBLIC_POSTHOG_HOST
3836

@@ -52,48 +50,32 @@ export function PostHogProvider({ children }: { children: React.ReactNode }) {
5250
)
5351
}
5452

55-
const initializePosthog = () => {
56-
if (!isInitialized) {
57-
posthog.init(posthogKey, {
58-
api_host: posthogHost || "https://us.i.posthog.com",
59-
capture_pageview: false,
60-
loaded: (posthogInstance) => {
61-
if (process.env.NODE_ENV === "development") {
62-
posthogInstance.debug()
63-
}
64-
},
65-
respect_dnt: true, // Respect Do Not Track
66-
})
67-
setIsInitialized(true)
68-
}
69-
}
70-
71-
// Check initial consent status
72-
if (hasConsent()) {
73-
initializePosthog()
74-
}
53+
// Check if user has already consented to cookies
54+
const userHasConsented = hasConsent()
7555

76-
// Listen for consent changes
77-
const unsubscribe = onConsentChange((consented) => {
78-
if (consented && !isInitialized) {
79-
initializePosthog()
80-
}
56+
// Initialize PostHog with appropriate persistence based on consent
57+
posthog.init(posthogKey, {
58+
api_host: posthogHost || "https://us.i.posthog.com",
59+
capture_pageview: false, // We handle pageview tracking manually
60+
loaded: (posthogInstance) => {
61+
if (process.env.NODE_ENV === "development") {
62+
posthogInstance.debug()
63+
}
64+
},
65+
save_referrer: true, // Save referrer information
66+
save_campaign_params: true, // Save UTM parameters
67+
respect_dnt: true, // Respect Do Not Track
68+
persistence: userHasConsented ? "localStorage+cookie" : "memory", // Use localStorage if consented, otherwise memory-only
69+
opt_out_capturing_by_default: false, // Start tracking immediately
8170
})
82-
83-
return () => {
84-
unsubscribe()
85-
}
8671
}
87-
}, [isInitialized])
72+
}, [])
8873

89-
// Only provide PostHog context if it's initialized
9074
return (
9175
<OriginalPostHogProvider client={posthog}>
92-
{isInitialized && (
93-
<Suspense fallback={null}>
94-
<PageViewTracker />
95-
</Suspense>
96-
)}
76+
<Suspense fallback={null}>
77+
<PageViewTracker />
78+
</Suspense>
9779
{children}
9880
</OriginalPostHogProvider>
9981
)

apps/web-roo-code/src/lib/analytics/consent-manager.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
import { getCookieConsentValue } from "react-cookie-consent"
77
import { CONSENT_COOKIE_NAME } from "@roo-code/types"
8+
import posthog from "posthog-js"
89

910
export const CONSENT_EVENT = "cookieConsentChanged"
1011

@@ -45,3 +46,27 @@ export function onConsentChange(callback: (consented: boolean) => void): () => v
4546
window.addEventListener(CONSENT_EVENT, handler)
4647
return () => window.removeEventListener(CONSENT_EVENT, handler)
4748
}
49+
50+
/**
51+
* Handle user accepting cookies
52+
* Opts PostHog back into cookie-based tracking
53+
*/
54+
export function handleConsentAccept(): void {
55+
if (typeof window !== "undefined" && posthog.__loaded) {
56+
// User accepted - ensure localStorage+cookie persistence is enabled
57+
posthog.opt_in_capturing()
58+
posthog.set_config({
59+
persistence: "localStorage+cookie",
60+
})
61+
}
62+
dispatchConsentEvent(true)
63+
}
64+
65+
/**
66+
* Handle user rejecting cookies
67+
* Switches PostHog to cookieless (memory-only) mode
68+
*/
69+
export function handleConsentReject(): void {
70+
// User rejected - stick to cookieless mode
71+
dispatchConsentEvent(false)
72+
}

src/api/providers/anthropic.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -230,17 +230,19 @@ export class AnthropicHandler extends BaseProvider implements SingleCompletionHa
230230
}
231231

232232
if (inputTokens > 0 || outputTokens > 0 || cacheWriteTokens > 0 || cacheReadTokens > 0) {
233+
const { totalCost } = calculateApiCostAnthropic(
234+
this.getModel().info,
235+
inputTokens,
236+
outputTokens,
237+
cacheWriteTokens,
238+
cacheReadTokens,
239+
)
240+
233241
yield {
234242
type: "usage",
235243
inputTokens: 0,
236244
outputTokens: 0,
237-
totalCost: calculateApiCostAnthropic(
238-
this.getModel().info,
239-
inputTokens,
240-
outputTokens,
241-
cacheWriteTokens,
242-
cacheReadTokens,
243-
),
245+
totalCost,
244246
}
245247
}
246248
}

src/api/providers/cerebras.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ export class CerebrasHandler extends BaseProvider implements SingleCompletionHan
331331
const { info } = this.getModel()
332332
// Use actual token usage from the last request
333333
const { inputTokens, outputTokens } = this.lastUsage
334-
return calculateApiCostOpenAI(info, inputTokens, outputTokens)
334+
const { totalCost } = calculateApiCostOpenAI(info, inputTokens, outputTokens)
335+
return totalCost
335336
}
336337
}

src/api/providers/deepinfra.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,9 +131,9 @@ export class DeepInfraHandler extends RouterProvider implements SingleCompletion
131131
const cacheWriteTokens = usage?.prompt_tokens_details?.cache_write_tokens || 0
132132
const cacheReadTokens = usage?.prompt_tokens_details?.cached_tokens || 0
133133

134-
const totalCost = modelInfo
134+
const { totalCost } = modelInfo
135135
? calculateApiCostOpenAI(modelInfo, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens)
136-
: 0
136+
: { totalCost: 0 }
137137

138138
return {
139139
type: "usage",

src/api/providers/groq.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export class GroqHandler extends BaseOpenAiCompatibleProvider<GroqModelId> {
6464
const cacheWriteTokens = 0
6565

6666
// Calculate cost using OpenAI-compatible cost calculation
67-
const totalCost = calculateApiCostOpenAI(info, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens)
67+
const { totalCost } = calculateApiCostOpenAI(info, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens)
6868

6969
yield {
7070
type: "usage",

src/api/providers/lite-llm.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -165,22 +165,23 @@ export class LiteLLMHandler extends RouterProvider implements SingleCompletionHa
165165
(lastUsage as any).prompt_cache_hit_tokens ||
166166
0
167167

168+
const { totalCost } = calculateApiCostOpenAI(
169+
info,
170+
lastUsage.prompt_tokens || 0,
171+
lastUsage.completion_tokens || 0,
172+
cacheWriteTokens,
173+
cacheReadTokens,
174+
)
175+
168176
const usageData: ApiStreamUsageChunk = {
169177
type: "usage",
170178
inputTokens: lastUsage.prompt_tokens || 0,
171179
outputTokens: lastUsage.completion_tokens || 0,
172180
cacheWriteTokens: cacheWriteTokens > 0 ? cacheWriteTokens : undefined,
173181
cacheReadTokens: cacheReadTokens > 0 ? cacheReadTokens : undefined,
182+
totalCost,
174183
}
175184

176-
usageData.totalCost = calculateApiCostOpenAI(
177-
info,
178-
usageData.inputTokens,
179-
usageData.outputTokens,
180-
usageData.cacheWriteTokens || 0,
181-
usageData.cacheReadTokens || 0,
182-
)
183-
184185
yield usageData
185186
}
186187
} catch (error) {

src/api/providers/openai-native.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ export class OpenAiNativeHandler extends BaseProvider implements SingleCompletio
9999
const effectiveInfo = this.applyServiceTierPricing(model.info, effectiveTier)
100100

101101
// Pass total input tokens directly to calculateApiCostOpenAI
102-
// The function handles subtracting both cache reads and writes internally (see shared/cost.ts:46)
103-
const totalCost = calculateApiCostOpenAI(
102+
// The function handles subtracting both cache reads and writes internally
103+
const { totalCost } = calculateApiCostOpenAI(
104104
effectiveInfo,
105105
totalInputTokens,
106106
totalOutputTokens,

src/api/providers/requesty.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,9 @@ export class RequestyHandler extends BaseProvider implements SingleCompletionHan
8585
const outputTokens = requestyUsage?.completion_tokens || 0
8686
const cacheWriteTokens = requestyUsage?.prompt_tokens_details?.caching_tokens || 0
8787
const cacheReadTokens = requestyUsage?.prompt_tokens_details?.cached_tokens || 0
88-
const totalCost = modelInfo
88+
const { totalCost } = modelInfo
8989
? calculateApiCostOpenAI(modelInfo, inputTokens, outputTokens, cacheWriteTokens, cacheReadTokens)
90-
: 0
90+
: { totalCost: 0 }
9191

9292
return {
9393
type: "usage",

0 commit comments

Comments
 (0)