(
input: CreateProjectInput,
userId: string,
)
| 159 | // after the first has landed. |
| 160 | const project = await queueForUser(userId, async () => { |
| 161 | await this.assertUnderQuota(userId); |
| 162 | return this.reserveProject(input, userId); |
| 163 | }); |
| 164 | |
| 165 | // handle project name generation if needed (this is the only sync operation we need) |
| 166 | let projectName = input.projectName; |
| 167 | if (!projectName || projectName === '') { |
| 168 | this.logger.debug( |
| 169 | 'Project name not provided in input, generating project name', |
| 170 | ); |
| 171 | |
| 172 | const result = await generateText({ |
| 173 | model: modelFor(input.model || DEFAULT_MODEL), |
| 174 | // A title is a handful of tokens. Left unset the SDK asks for the |
| 175 | // model's full output window — 64k for Sonnet — which providers |
| 176 | // reserve credit against and reject outright on a small balance. |
| 177 | maxOutputTokens: 64, |
| 178 | // ai@7 rejects role:'system' inside `messages`; instructions is the |
| 179 | // replacement, and the old shape threw before reaching the model. |
| 180 | instructions: |
| 181 | 'You are a specialized project name generator. Create a concise, descriptive project title (max 20 words) based on the description. Respond ONLY with the project name, no explanation.', |
| 182 | prompt: `Generate a project name for: ${input.description}`, |
| 183 | }); |
| 184 | |
| 185 | projectName = result.text.trim(); |
| 186 | this.logger.debug(`Generated project name: ${projectName}`); |
| 187 | } |
| 188 | |
| 189 | // Create chat with proper title |
| 190 | const defaultChat = await this.chatService.createChatWithMessage(userId, { |
| 191 | title: projectName || 'New Project Chat', |
| 192 | message: input.description, |
| 193 | model: input.model, |
| 194 | }); |
| 195 | |
| 196 | // The row already exists (reserved above, under the quota queue); this |
| 197 | // names it and materialises its working directory. |
| 198 | project.projectName = projectName; |
| 199 | this.createProjectInBackground(input, project, defaultChat); |
| 200 | |
| 201 | // Return chat immediately so user can start interacting |
| 202 | return defaultChat; |
| 203 | } catch (error) { |
| 204 | this.logger.error( |
| 205 | `Error in createProject: ${error.message}`, |
| 206 | error.stack, |
| 207 | ); |
| 208 | throw new InternalServerErrorException('Error creating the project.'); |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | /** |
| 213 | * Claim the user's quota slot by saving the row, before anything slow. |
| 214 | * |
| 215 | * Split out of the background task so the count that authorises it and the |
| 216 | * row that consumes it happen in the same queued turn — see the comment at |
| 217 | * the call site. Named unnamed: the title comes from a model call that |
| 218 | * must not hold the queue. |
nothing calls this directly
no test coverage detected