1- import { Client } from '@gravity-ai/api '
1+ import { WEBSITE_URL } from '@codebuff/sdk '
22import { useCallback , useEffect , useRef , useState } from 'react'
33
44import { getAdsEnabled } from '../commands/ads'
55import { useChatStore } from '../state/chat-store'
6- import { getCliEnv } from '../utils/env '
6+ import { getAuthToken } from '../utils/auth '
77import { logger } from '../utils/logger'
88
9- import type { AdResponse } from '@gravity-ai/api'
109import type { Message } from '@codebuff/common/types/messages/codebuff-message'
1110
1211const MAX_MESSAGES_FOR_AD = 100
@@ -16,6 +15,17 @@ const MAX_ADS_AFTER_ACTIVITY = 3 // Show up to 3 ads after last activity, then s
1615
1716type AdMessage = { role : 'user' | 'assistant' ; content : string }
1817
18+ // Ad response type (matches Gravity API response)
19+ export type AdResponse = {
20+ adText : string
21+ title : string
22+ url : string
23+ favicon : string
24+ clickUrl : string
25+ impUrl : string
26+ payout : number
27+ }
28+
1929/**
2030 * Extract text content from a Message's content array
2131 */
@@ -71,7 +81,6 @@ export type GravityAdState = {
7181export const useGravityAd = ( ) : GravityAdState => {
7282 const [ ad , setAd ] = useState < AdResponse | null > ( null )
7383 const [ isLoading , setIsLoading ] = useState ( false )
74- const clientRef = useRef < Client | null > ( null )
7584 const impressionFiredRef = useRef < Set < string > > ( new Set ( ) )
7685
7786 // Pre-fetched next ad ready to display
@@ -93,29 +102,42 @@ export const useGravityAd = (): GravityAdState => {
93102 // Get runState from chat store
94103 const runState = useChatStore ( ( state ) => state . runState )
95104
96- // Initialize client on mount
97- useEffect ( ( ) => {
98- const apiKey = getCliEnv ( ) . GRAVITY_API_KEY
99- logger . info (
100- { hasApiKey : ! ! apiKey , adsEnabled : getAdsEnabled ( ) } ,
101- '[gravity] Initializing Gravity ad client' ,
102- )
103- if ( apiKey ) {
104- clientRef . current = new Client ( apiKey )
105- logger . info ( '[gravity] Gravity client initialized successfully' )
106- } else {
107- logger . warn ( '[gravity] No GRAVITY_API_KEY found in environment' )
108- }
109- } , [ ] )
110-
111- // Fire impression when ad changes
105+ // Fire impression via web API when ad changes (grants credits)
112106 useEffect ( ( ) => {
113107 if ( ad ?. impUrl && ! impressionFiredRef . current . has ( ad . impUrl ) ) {
114108 impressionFiredRef . current . add ( ad . impUrl )
115- logger . info ( { impUrl : ad . impUrl } , '[gravity] Firing ad impression' )
116- fetch ( ad . impUrl ) . catch ( ( err ) => {
117- logger . debug ( { err } , '[gravity] Failed to fire ad impression' )
109+ logger . info ( { impUrl : ad . impUrl , payout : ad . payout } , '[gravity] Recording ad impression' )
110+
111+ const authToken = getAuthToken ( )
112+ if ( ! authToken ) {
113+ logger . warn ( '[gravity] No auth token, skipping impression recording' )
114+ return
115+ }
116+
117+ // Call our web API to fire impression and grant credits
118+ fetch ( `${ WEBSITE_URL } /api/v1/ads/impression` , {
119+ method : 'POST' ,
120+ headers : {
121+ 'Content-Type' : 'application/json' ,
122+ 'Authorization' : `Bearer ${ authToken } ` ,
123+ } ,
124+ body : JSON . stringify ( {
125+ impUrl : ad . impUrl ,
126+ payout : ad . payout ,
127+ } ) ,
118128 } )
129+ . then ( ( res ) => res . json ( ) )
130+ . then ( ( data ) => {
131+ if ( data . creditsGranted > 0 ) {
132+ logger . info (
133+ { creditsGranted : data . creditsGranted } ,
134+ '[gravity] Ad impression credits granted' ,
135+ )
136+ }
137+ } )
138+ . catch ( ( err ) => {
139+ logger . debug ( { err } , '[gravity] Failed to record ad impression' )
140+ } )
119141 }
120142 } , [ ad ] )
121143
@@ -131,10 +153,15 @@ export const useGravityAd = (): GravityAdState => {
131153 }
132154 } , [ ] )
133155
134- // Fetch an ad and return it (for pre-fetching)
156+ // Fetch an ad via web API and return it (for pre-fetching)
135157 const fetchAdAsync = useCallback ( async ( ) : Promise < AdResponse | null > => {
136- const client = clientRef . current
137- if ( ! client || ! getAdsEnabled ( ) ) return null
158+ if ( ! getAdsEnabled ( ) ) return null
159+
160+ const authToken = getAuthToken ( )
161+ if ( ! authToken ) {
162+ logger . warn ( '[gravity] No auth token available' )
163+ return null
164+ }
138165
139166 const currentRunState = useChatStore . getState ( ) . runState
140167 const messageHistory =
@@ -143,22 +170,38 @@ export const useGravityAd = (): GravityAdState => {
143170
144171 if ( adMessages . length === 0 ) return null
145172
146- logger . info ( '[gravity] Fetching ad from Gravity API' )
173+ logger . info ( '[gravity] Fetching ad from web API' )
147174
148175 try {
149- const response = await client . getAd ( { messages : adMessages } )
176+ const response = await fetch ( `${ WEBSITE_URL } /api/v1/ads` , {
177+ method : 'POST' ,
178+ headers : {
179+ 'Content-Type' : 'application/json' ,
180+ 'Authorization' : `Bearer ${ authToken } ` ,
181+ } ,
182+ body : JSON . stringify ( { messages : adMessages } ) ,
183+ } )
184+
185+ if ( ! response . ok ) {
186+ logger . warn ( { status : response . status } , '[gravity] Web API returned error' )
187+ return null
188+ }
189+
190+ const data = await response . json ( )
191+ const ad = data . ad as AdResponse | null
192+
150193 logger . info (
151194 {
152- hasAd : ! ! response ,
153- adText : response ?. adText ,
154- title : ( response as { title ?: string } ) ?. title ,
155- clickUrl : response ?. clickUrl ,
156- impUrl : response ?. impUrl ,
157- payout : response ?. payout ,
195+ hasAd : ! ! ad ,
196+ adText : ad ?. adText ,
197+ title : ad ?. title ,
198+ clickUrl : ad ?. clickUrl ,
199+ impUrl : ad ?. impUrl ,
200+ payout : ad ?. payout ,
158201 } ,
159202 '[gravity] Received ad response' ,
160203 )
161- return response
204+ return ad
162205 } catch ( err ) {
163206 logger . error ( { err } , '[gravity] Failed to fetch ad' )
164207 return null
@@ -231,9 +274,9 @@ export const useGravityAd = (): GravityAdState => {
231274 runState ?. sessionState ?. mainAgentState ?. messageHistory ?? [ ]
232275 const hasMessages = messageHistory . length > 0
233276 const adsEnabled = getAdsEnabled ( )
234- const hasClient = ! ! clientRef . current
277+ const hasAuth = ! ! getAuthToken ( )
235278
236- if ( hasMessages && adsEnabled && hasClient && ! isStartedRef . current ) {
279+ if ( hasMessages && adsEnabled && hasAuth && ! isStartedRef . current ) {
237280 logger . info ( '[gravity] Starting ad rotation' )
238281 isStartedRef . current = true
239282 setIsLoading ( true )
0 commit comments