@@ -9,12 +9,14 @@ import io.tolgee.dtos.LlmParams
99import io.tolgee.dtos.request.prompt.PromptDto
1010import io.tolgee.dtos.request.prompt.PromptRunDto
1111import io.tolgee.ee.component.PromptLazyMap
12+ import io.tolgee.ee.service.LlmProviderResolver
1213import io.tolgee.ee.service.LlmProviderService
1314import io.tolgee.ee.service.prompt.PromptResultParser.ParsedResult
1415import io.tolgee.events.OnAfterMachineTranslationEvent
1516import io.tolgee.events.OnBeforeMachineTranslationEvent
1617import io.tolgee.exceptions.BadRequestException
1718import io.tolgee.exceptions.FailedDependencyException
19+ import io.tolgee.exceptions.LlmProviderNotFoundException
1820import io.tolgee.exceptions.LlmRateLimitedException
1921import io.tolgee.exceptions.NotFoundException
2022import io.tolgee.exceptions.TooManyRequestsException
@@ -26,6 +28,7 @@ import io.tolgee.service.PromptService
2628import io.tolgee.service.key.KeyService
2729import io.tolgee.service.machineTranslation.MtServiceConfigService
2830import io.tolgee.service.project.ProjectService
31+ import jakarta.persistence.EntityManager
2932import org.springframework.context.ApplicationContext
3033import org.springframework.context.annotation.Lazy
3134import org.springframework.context.annotation.Primary
@@ -49,13 +52,15 @@ class PromptServiceEeImpl(
4952 private val applicationContext : ApplicationContext ,
5053 private val promptParamsHelper : PromptParamsHelper ,
5154 private val objectMapper : ObjectMapper ,
55+ private val llmProviderResolver : LlmProviderResolver ,
56+ private val entityManager : EntityManager ,
5257) : PromptService {
5358 fun getAllPaged (
5459 projectId : Long ,
5560 pageable : Pageable ,
5661 search : String? ,
5762 ): Page <Prompt > {
58- return promptRepository.getAllPaged(projectId, pageable, search)
63+ return promptRepository.getAllPaged(projectId, pageable, search).map { withResolvedProviderName(it) }
5964 }
6065
6166 fun createPrompt (
@@ -71,7 +76,7 @@ class PromptServiceEeImpl(
7176 basicPromptOptions = dto.basicPromptOptions?.toTypedArray(),
7277 )
7378 promptRepository.save(prompt)
74- return prompt
79+ return withResolvedProviderName( prompt)
7580 }
7681
7782 override fun findPromptOrDefaultDto (
@@ -98,6 +103,13 @@ class PromptServiceEeImpl(
98103 return promptRepository.findPrompt(projectId, promptId) ? : throw NotFoundException (Message .PROMPT_NOT_FOUND )
99104 }
100105
106+ fun findPromptWithResolvedProvider (
107+ projectId : Long ,
108+ promptId : Long ,
109+ ): Prompt {
110+ return withResolvedProviderName(findPrompt(projectId, promptId))
111+ }
112+
101113 override fun deleteAllByProjectId (projectId : Long ) {
102114 return promptRepository.deleteAllByProjectId(projectId)
103115 }
@@ -113,7 +125,7 @@ class PromptServiceEeImpl(
113125 prompt.providerName = dto.providerName
114126 prompt.basicPromptOptions = dto.basicPromptOptions?.toTypedArray()
115127 promptRepository.save(prompt)
116- return prompt
128+ return withResolvedProviderName( prompt)
117129 }
118130
119131 fun deletePrompt (
@@ -272,4 +284,30 @@ class PromptServiceEeImpl(
272284 OnAfterMachineTranslationEvent (this , organizationId, actualPriceInCents),
273285 )
274286 }
287+
288+ /* *
289+ * Resolves the provider name through the fallback chain before returning to the API.
290+ *
291+ * Prompts store the original provider name in the DB. When a provider is delisted and
292+ * replaced by a successor (e.g. "gpt-4o" -> "gpt-4.1") with a fallback configured,
293+ * the stored name becomes stale. This method resolves it so the frontend sees a provider
294+ * name that actually exists among the available providers.
295+ *
296+ * The entity is detached before mutation to prevent OSIV from flushing the resolved name
297+ * back to the DB — the stored value must remain the original.
298+ */
299+ private fun withResolvedProviderName (prompt : Prompt ): Prompt {
300+ if (prompt.providerName.isEmpty()) return prompt
301+ // Access lazy association while still managed
302+ val organizationId = prompt.project.organizationOwner.id
303+ val resolvedName =
304+ try {
305+ llmProviderResolver.resolveProviderName(organizationId, prompt.providerName)
306+ } catch (e: LlmProviderNotFoundException ) {
307+ prompt.providerName
308+ }
309+ entityManager.detach(prompt)
310+ prompt.providerName = resolvedName
311+ return prompt
312+ }
275313}
0 commit comments