11import { chat } from '@tanstack/ai'
2+ import { anthropicText } from '@tanstack/ai-anthropic'
23import { openaiText } from '@tanstack/ai-openai'
34import { z } from 'zod'
45import {
@@ -52,6 +53,11 @@ type ApplicationStarterAnalysisPlan = z.infer<
5253 typeof applicationStarterAnalysisSchema
5354>
5455type OpenAIStarterModel = Parameters < typeof openaiText > [ 0 ]
56+ type AnthropicStarterModel = Parameters < typeof anthropicText > [ 0 ]
57+ type ApplicationStarterPromptModel = OpenAIStarterModel | AnthropicStarterModel
58+ type ApplicationStarterPromptAdapter =
59+ | ReturnType < typeof openaiText >
60+ | ReturnType < typeof anthropicText >
5561
5662const applicationStarterAnalysisSchema = z . object ( {
5763 deployment : z . enum ( [ 'cloudflare' , 'netlify' , 'nitro' , 'railway' ] ) . nullable ( ) ,
@@ -61,7 +67,7 @@ const applicationStarterAnalysisSchema = z.object({
6167 template : z . string ( ) . trim ( ) . min ( 1 ) . max ( 80 ) . nullable ( ) ,
6268} )
6369
64- const supportedApplicationStarterModels = [
70+ const supportedApplicationStarterAnalysisModels = [
6571 'gpt-4.1' ,
6672 'gpt-4.1-mini' ,
6773 'gpt-4.1-nano' ,
@@ -72,6 +78,13 @@ const supportedApplicationStarterModels = [
7278 'gpt-5-nano' ,
7379] as const satisfies Array < OpenAIStarterModel >
7480
81+ const supportedApplicationStarterPromptModels = [
82+ ...supportedApplicationStarterAnalysisModels ,
83+ 'claude-haiku-4-5' ,
84+ 'claude-sonnet-4-5' ,
85+ 'claude-opus-4-5' ,
86+ ] as const satisfies Array < ApplicationStarterPromptModel >
87+
7588const deterministicProvider : ApplicationStarterProvider = {
7689 name : 'deterministic' ,
7790 resolve : async ( request ) =>
@@ -182,7 +195,7 @@ function getRequestedProviderName(): ApplicationStarterProviderName {
182195 return 'model'
183196 }
184197
185- return process . env . OPENAI_API_KEY ? 'model' : 'deterministic'
198+ return hasApplicationStarterModelKey ( ) ? 'model' : 'deterministic'
186199}
187200
188201async function resolveApplicationStarterWithModel (
@@ -228,16 +241,16 @@ async function tryGenerateApplicationStarterPrompt({
228241 request : ApplicationStarterRequest
229242 deterministicResult : ApplicationStarterResult
230243} ) : Promise < ApplicationStarterPromptPlan | null > {
231- const apiKey = process . env . OPENAI_API_KEY
244+ const adapter = getApplicationStarterPromptAdapter ( )
232245
233- if ( ! apiKey ) {
246+ if ( ! adapter ) {
234247 return null
235248 }
236249
237250 try {
238251 return applicationStarterPromptSchema . parse (
239252 await chat ( {
240- adapter : openaiText ( getApplicationStarterPromptModel ( ) ) ,
253+ adapter,
241254 maxTokens : 900 ,
242255 messages : [
243256 {
@@ -300,15 +313,29 @@ function getApplicationStarterAnalysisModel(): OpenAIStarterModel {
300313 } )
301314}
302315
303- function getApplicationStarterPromptModel ( ) : OpenAIStarterModel {
304- return getConfiguredApplicationStarterModel ( {
305- fallbackModel : 'gpt-4.1-mini' ,
316+ function getApplicationStarterPromptModel ( ) : ApplicationStarterPromptModel {
317+ return getConfiguredApplicationStarterPromptModel ( {
318+ fallbackModel : getDefaultApplicationStarterPromptModel ( ) ,
306319 requestedModel :
307320 process . env . APPLICATION_STARTER_PROMPT_MODEL ??
308321 process . env . APPLICATION_STARTER_MODEL ,
309322 } )
310323}
311324
325+ function getApplicationStarterPromptAdapter ( ) : ApplicationStarterPromptAdapter | null {
326+ const model = getApplicationStarterPromptModel ( )
327+
328+ if ( isAnthropicStarterModel ( model ) ) {
329+ return process . env . ANTHROPIC_API_KEY ? anthropicText ( model ) : null
330+ }
331+
332+ return process . env . OPENAI_API_KEY ? openaiText ( model ) : null
333+ }
334+
335+ function getDefaultApplicationStarterPromptModel ( ) : ApplicationStarterPromptModel {
336+ return 'gpt-4.1-mini'
337+ }
338+
312339function getConfiguredApplicationStarterModel ( {
313340 fallbackModel,
314341 requestedModel,
@@ -320,13 +347,41 @@ function getConfiguredApplicationStarterModel({
320347 return fallbackModel
321348 }
322349
323- const matchingModel = supportedApplicationStarterModels . find (
350+ const matchingModel = supportedApplicationStarterAnalysisModels . find (
351+ ( model ) => model === requestedModel ,
352+ )
353+
354+ return matchingModel ?? fallbackModel
355+ }
356+
357+ function getConfiguredApplicationStarterPromptModel ( {
358+ fallbackModel,
359+ requestedModel,
360+ } : {
361+ fallbackModel : ApplicationStarterPromptModel
362+ requestedModel : string | undefined
363+ } ) {
364+ if ( ! requestedModel ) {
365+ return fallbackModel
366+ }
367+
368+ const matchingModel = supportedApplicationStarterPromptModels . find (
324369 ( model ) => model === requestedModel ,
325370 )
326371
327372 return matchingModel ?? fallbackModel
328373}
329374
375+ function isAnthropicStarterModel (
376+ model : ApplicationStarterPromptModel ,
377+ ) : model is AnthropicStarterModel {
378+ return model . startsWith ( 'claude-' )
379+ }
380+
381+ function hasApplicationStarterModelKey ( ) {
382+ return Boolean ( process . env . OPENAI_API_KEY || process . env . ANTHROPIC_API_KEY )
383+ }
384+
330385function buildPromptGenerationRequest ( {
331386 request,
332387 deterministicResult,
0 commit comments