@@ -33,6 +33,7 @@ You are the official AI assistant for Bits&Bytes, a teen-led code club based in
3333- **Mission:** Innovation, collaboration, and real-world impact through technology.
3434- **Activities:** Hackathons (e.g., Scrapyard Lucknow), workshops, and student mentorship.
3535- **Contact:** hello@gobitsnbytes.org
36+ - **GitHub:** https://github.com/gobitsnbytes
3637
3738**How to get answers:**
38391. **For Team/Roles:** DO NOT guess. Always use the 'find_team_expert' or 'recommend_role' tools. The team structure is dynamic.
@@ -51,6 +52,8 @@ You are the official AI assistant for Bits&Bytes, a teen-led code club based in
5152
5253Rules:
5354- Always stay truthful to Bits&Bytes.
55+ - Be extremely concise, conversational, and direct. Avoid long, multi-paragraph summaries or filler text. Get straight to the point.
56+ - Do not use 'suggest_navigation' and 'highlight_text' in the exact same response. If you navigate the user to a new page, wait for them to see it; do not highlight right away since the page will be loading.
5457- If you can't find the answer in the tools or page content, admit it:
5558 "I’m not sure about that based on the information publicly available on this site."
5659`
@@ -309,104 +312,95 @@ export async function POST(req: NextRequest) {
309312 messages,
310313 tools,
311314 tool_choice : "auto" ,
312- max_completion_tokens : 400 ,
315+ max_tokens : 400 ,
313316 } )
314317 }
315318
316319 let modelUsed = PRIMARY_MODEL
317- let completion
320+ let currentMessages = [ ...baseMessages ]
321+ let actionToClient : AssistantAction | undefined
318322
319- try {
320- completion = await runCompletion ( PRIMARY_MODEL , baseMessages )
321- } catch ( err ) {
322- const apiError = err as APIError
323- const code = ( apiError as any ) ?. code ?? ( apiError as any ) ?. error ?. code
324- const status = ( apiError as any ) ?. status
325- const shouldFallback =
326- code === "model_not_found" ||
327- code === "unsupported_parameter" ||
328- code === "unsupported_value" ||
329- status === 403
330-
331- if ( shouldFallback ) {
332- modelUsed = FALLBACK_MODEL
333- completion = await runCompletion ( FALLBACK_MODEL , baseMessages )
334- } else {
335- throw err
323+ for ( let i = 0 ; i < 5 ; i ++ ) {
324+ let completion
325+ try {
326+ completion = await runCompletion ( PRIMARY_MODEL , currentMessages )
327+ } catch ( err ) {
328+ const apiError = err as APIError
329+ const code = ( apiError as any ) ?. code ?? ( apiError as any ) ?. error ?. code
330+ const status = ( apiError as any ) ?. status
331+ const shouldFallback =
332+ code === "model_not_found" ||
333+ code === "unsupported_parameter" ||
334+ code === "unsupported_value" ||
335+ status === 403
336+
337+ if ( shouldFallback ) {
338+ modelUsed = FALLBACK_MODEL
339+ completion = await runCompletion ( FALLBACK_MODEL , currentMessages )
340+ } else {
341+ throw err
342+ }
336343 }
337- }
338344
339- const choice = completion . choices [ 0 ]
340- const message = choice ?. message
345+ const choice = completion . choices [ 0 ]
346+ const message = choice ?. message
341347
342- // If no tool calls, stream the final answer directly.
343- if ( ! message ?. tool_calls || message . tool_calls . length === 0 ) {
344- try {
345- return await streamAssistantResponse ( modelUsed , baseMessages )
346- } catch ( streamErr ) {
347- console . error ( "Assistant stream error:" , streamErr )
348- const answer = message ?. content ?. trim ( )
349- return NextResponse . json ( {
350- answer : answer ?? "I’m not sure about that based on the information publicly available on this site." ,
351- } )
348+ if ( ! message ?. tool_calls || message . tool_calls . length === 0 ) {
349+ break // No more tool calls required
352350 }
353- }
354351
355- // Handle first tool call for now (this already gives agentic behaviour).
356- const toolCall = message . tool_calls [ 0 ]
357- const toolName = toolCall . function . name
358- let toolArgs : any = { }
352+ currentMessages . push ( message )
359353
360- try {
361- toolArgs = toolCall . function . arguments ? JSON . parse ( toolCall . function . arguments ) : { }
362- } catch {
363- toolArgs = { }
364- }
354+ for ( const toolCall of message . tool_calls ) {
355+ const toolName = toolCall . function . name
356+ let toolArgs : any = { }
357+ try {
358+ toolArgs = toolCall . function . arguments ? JSON . parse ( toolCall . function . arguments ) : { }
359+ } catch {
360+ toolArgs = { }
361+ }
365362
366- let toolResult : any = null
367- let action : AssistantAction | undefined
368-
369- if ( toolName === "submit_contact_form" ) {
370- toolResult = await handleSubmitContactTool ( toolArgs )
371- } else if ( toolName === "suggest_navigation" ) {
372- const path = normalizePath ( toolArgs ?. path )
373- toolResult = { success : true , path }
374- action = { type : "navigate" as const , path }
375- } else if ( toolName === "get_site_section" ) {
376- toolResult = await handleGetSiteSectionTool ( toolArgs ?. section ?? "home" , req )
377- } else if ( toolName === "find_team_expert" ) {
378- const query = ( toolArgs ?. query ?? "" ) . toString ( )
379- const experts = findExperts ( query )
380- toolResult = { query, experts }
381- } else if ( toolName === "recommend_role" ) {
382- const skills = Array . isArray ( toolArgs ?. skills ) ? toolArgs . skills : [ ]
383- const interests = Array . isArray ( toolArgs ?. interests ) ? toolArgs . interests : [ ]
384- const recommendation = recommendRoles ( skills , interests )
385- toolResult = { skills, interests, recommendation }
386- } else if ( toolName === "highlight_text" ) {
387- const textSnippet = ( toolArgs ?. textSnippet ?? "" ) . toString ( )
388- toolResult = { success : true , textSnippet }
389- action = { type : "highlight" as const , textSnippet }
390- } else {
391- toolResult = { success : false , message : `Unknown tool: ${ toolName } ` }
392- }
363+ let toolResult : any = null
364+
365+ if ( toolName === "submit_contact_form" ) {
366+ toolResult = await handleSubmitContactTool ( toolArgs )
367+ } else if ( toolName === "suggest_navigation" ) {
368+ const path = normalizePath ( toolArgs ?. path )
369+ toolResult = { success : true , path }
370+ actionToClient = { type : "navigate" as const , path }
371+ } else if ( toolName === "get_site_section" ) {
372+ toolResult = await handleGetSiteSectionTool ( toolArgs ?. section ?? "home" , req )
373+ } else if ( toolName === "find_team_expert" ) {
374+ const query = ( toolArgs ?. query ?? "" ) . toString ( )
375+ const experts = findExperts ( query )
376+ toolResult = { query, experts }
377+ } else if ( toolName === "recommend_role" ) {
378+ const skills = Array . isArray ( toolArgs ?. skills ) ? toolArgs . skills : [ ]
379+ const interests = Array . isArray ( toolArgs ?. interests ) ? toolArgs . interests : [ ]
380+ const recommendation = recommendRoles ( skills , interests )
381+ toolResult = { skills, interests, recommendation }
382+ } else if ( toolName === "highlight_text" ) {
383+ const textSnippet = ( toolArgs ?. textSnippet ?? "" ) . toString ( )
384+ toolResult = { success : true , textSnippet }
385+ actionToClient = { type : "highlight" as const , textSnippet }
386+ } else {
387+ toolResult = { success : false , message : `Unknown tool: ${ toolName } ` }
388+ }
393389
394- const messagesWithTool : OpenAI . Chat . ChatCompletionMessageParam [ ] = [
395- ...baseMessages ,
396- message ,
397- {
398- role : "tool" ,
399- tool_call_id : toolCall . id ,
400- content : JSON . stringify ( toolResult ) ,
401- } as OpenAI . Chat . ChatCompletionToolMessageParam ,
402- ]
390+ currentMessages . push ( {
391+ role : "tool" ,
392+ tool_call_id : toolCall . id ,
393+ content : JSON . stringify ( toolResult ) ,
394+ } as OpenAI . Chat . ChatCompletionToolMessageParam )
395+ }
396+ }
403397
404398 try {
405- return await streamAssistantResponse ( modelUsed , messagesWithTool , action )
399+ return await streamAssistantResponse ( modelUsed , currentMessages , actionToClient )
406400 } catch ( streamErr ) {
407401 console . error ( "Assistant stream error after tool call:" , streamErr )
408402 return NextResponse . json (
409- { error : "Failed to stream the assistant response after tool call ." } ,
403+ { error : "Failed to stream the assistant response." } ,
410404 { status : 500 }
411405 )
412406 }
@@ -427,7 +421,7 @@ async function streamAssistantResponse(
427421 const completion = await openai . chat . completions . create ( {
428422 model,
429423 messages,
430- max_completion_tokens : 400 ,
424+ max_tokens : 400 ,
431425 stream : true ,
432426 } )
433427
@@ -444,6 +438,7 @@ async function streamAssistantResponse(
444438 try {
445439 for await ( const part of completion ) {
446440 const delta = part . choices [ 0 ] ?. delta
441+
447442 if ( delta ?. content ) {
448443 send ( { type : "token" , content : delta . content } )
449444 }
0 commit comments