@@ -8,20 +8,23 @@ import { buildArray } from '@codebuff/common/util/array'
88import { getErrorObject } from '@codebuff/common/util/error'
99import { convertCbToModelMessages } from '@codebuff/common/util/messages'
1010import { StopSequenceHandler } from '@codebuff/common/util/stop-sequence'
11- import { streamText , APICallError , generateText } from 'ai'
11+ import { streamText , APICallError , generateText , generateObject } from 'ai'
1212
1313import { WEBSITE_URL } from '../constants'
1414
1515import type { LanguageModelV2 } from '@ai-sdk/provider'
1616import type {
1717 PromptAiSdkFn ,
1818 PromptAiSdkStreamFn ,
19+ PromptAiSdkStructuredInput ,
20+ PromptAiSdkStructuredOutput ,
1921} from '@codebuff/common/types/contracts/llm'
2022import type { ParamsOf } from '@codebuff/common/types/function-params'
2123import type {
2224 OpenRouterProviderOptions ,
2325 OpenRouterUsageAccounting ,
2426} from '@openrouter/ai-sdk-provider'
27+ import type z from 'zod/v4'
2528
2629function getAiSdkModel ( params : {
2730 apiKey : string
@@ -286,22 +289,79 @@ export async function promptAiSdk(
286289 return content
287290}
288291
289- console . log (
290- await promptAiSdk ( {
291- apiKey : '12345' ,
292- messages : [ { role : 'user' , content : 'Hello' } ] ,
293- clientSessionId : 'test-session' ,
294- fingerprintId : 'test-fingerprint' ,
295- model : 'openai/gpt-5' ,
296- userId : 'test-user-id' ,
297- userInputId : '64a2e61f-1fab-4701-8651-7ff7a473e97a' ,
298- sendAction : ( ) => { } ,
299- logger : console ,
300- trackEvent : ( ) => { } ,
301- liveUserInputRecord : {
302- 'test-user-id' : [ '64a2e61f-1fab-4701-8651-7ff7a473e97a' ] ,
292+ export async function promptAiSdkStructured < T > (
293+ params : PromptAiSdkStructuredInput < T > ,
294+ ) : PromptAiSdkStructuredOutput < T > {
295+ const { logger } = params
296+
297+ if ( ! checkLiveUserInput ( params ) ) {
298+ logger . info (
299+ {
300+ userId : params . userId ,
301+ userInputId : params . userInputId ,
302+ liveUserInputId : getLiveUserInputIds ( params ) ,
303+ } ,
304+ 'Skipping structured prompt due to canceled user input' ,
305+ )
306+ return { } as T
307+ }
308+ let aiSDKModel = getAiSdkModel ( params )
309+
310+ const response = await generateObject < z . ZodType < T > , 'object' > ( {
311+ ...params ,
312+ prompt : undefined ,
313+ model : aiSDKModel ,
314+ output : 'object' ,
315+ messages : convertCbToModelMessages ( params ) ,
316+ providerOptions : {
317+ codebuff : {
318+ codebuff_metadata : {
319+ run_id : params . userInputId ,
320+ client_id : params . clientSessionId ,
321+ } ,
322+ } ,
303323 } ,
304- sessionConnections : { 'test-session' : true } ,
305- } ) ,
306- 'asdf' ,
307- )
324+ } )
325+
326+ const content = response . object
327+
328+ const messageId = response . response . id
329+ const providerMetadata = response . providerMetadata ?? { }
330+ const usage = response . usage
331+ let inputTokens = usage . inputTokens || 0
332+ const outputTokens = usage . outputTokens || 0
333+ let cacheReadInputTokens : number = 0
334+ let cacheCreationInputTokens : number = 0
335+ let costOverrideDollars : number | undefined
336+ if ( providerMetadata . anthropic ) {
337+ cacheReadInputTokens =
338+ typeof providerMetadata . anthropic . cacheReadInputTokens === 'number'
339+ ? providerMetadata . anthropic . cacheReadInputTokens
340+ : 0
341+ cacheCreationInputTokens =
342+ typeof providerMetadata . anthropic . cacheCreationInputTokens === 'number'
343+ ? providerMetadata . anthropic . cacheCreationInputTokens
344+ : 0
345+ }
346+ if ( providerMetadata . openrouter ) {
347+ if ( providerMetadata . openrouter . usage ) {
348+ const openrouterUsage = providerMetadata . openrouter
349+ . usage as OpenRouterUsageAccounting
350+ cacheReadInputTokens =
351+ openrouterUsage . promptTokensDetails ?. cachedTokens ?? 0
352+ inputTokens = openrouterUsage . promptTokens - cacheReadInputTokens
353+
354+ costOverrideDollars =
355+ ( openrouterUsage . cost ?? 0 ) +
356+ ( openrouterUsage . costDetails ?. upstreamInferenceCost ?? 0 )
357+ }
358+ }
359+
360+ // Call the cost callback if provided
361+ if ( params . onCostCalculated && costOverrideDollars ) {
362+ const creditsUsed = costOverrideDollars * ( 1 + PROFIT_MARGIN )
363+ await params . onCostCalculated ( creditsUsed )
364+ }
365+
366+ return content
367+ }
0 commit comments