Skip to content

Commit 56818ca

Browse files
feat(DERCBOT-1829): add llm reasonning effort
see above
1 parent e022f9f commit 56818ca

File tree

15 files changed

+443
-22
lines changed

15 files changed

+443
-22
lines changed

bot/admin/web/src/app/configuration/sentence-generation-settings/models/engines-configuration.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import {
2121
OllamaLlmModelsList,
2222
OpenAIModelsList,
2323
ProvidersConfigurationParam,
24-
PromptDefinitionFormatter
24+
PromptDefinitionFormatter,
25+
ReasoningEffortValues
2526
} from '../../../shared/model/ai-settings';
2627

2728
export const DefaultPrompt: string = `# Sentences generation instructions
@@ -83,7 +84,8 @@ export const EngineConfigurations: EnginesConfiguration[] = [
8384
{ key: 'apiKey', label: 'Api key', type: 'obfuscated', confirmExport: true },
8485
{ key: 'baseUrl', label: 'Base url', type: 'text', defaultValue: 'https://api.openai.com/v1' },
8586
{ key: 'model', label: 'Model name', type: 'openlist', source: OpenAIModelsList },
86-
{ key: 'temperature', label: 'Temperature', type: 'number', inputScale: 'fullwidth', min: 0, max: 1, step: 0.05 }
87+
{ key: 'temperature', label: 'Temperature', type: 'number', inputScale: 'fullwidth', min: 0, max: 1, step: 0.05 },
88+
{ key: 'reasoningEffort', label: 'Reasoning effort', type: 'openlist', source: ReasoningEffortValues, defaultValue: 'low' }
8789
]
8890
},
8991
{
@@ -95,7 +97,8 @@ export const EngineConfigurations: EnginesConfiguration[] = [
9597
{ key: 'deploymentName', label: 'Deployment name', type: 'text' },
9698
{ key: 'model', label: 'Model name', type: 'openlist', source: OpenAIModelsList },
9799
{ key: 'apiBase', label: 'Base url', type: 'obfuscated' },
98-
{ key: 'temperature', label: 'Temperature', type: 'number', inputScale: 'fullwidth', min: 0, max: 1, step: 0.05 }
100+
{ key: 'temperature', label: 'Temperature', type: 'number', inputScale: 'fullwidth', min: 0, max: 1, step: 0.05 },
101+
{ key: 'reasoningEffort', label: 'Reasoning effort', type: 'openlist', source: ReasoningEffortValues, defaultValue: 'low' }
99102
]
100103
},
101104
{

bot/admin/web/src/app/rag/rag-settings/models/engines-configurations.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ import {
2424
OpenAIEmbeddingModel,
2525
OpenAIModelsList,
2626
ProvidersConfigurationParam,
27-
PromptDefinitionFormatter
27+
PromptDefinitionFormatter,
28+
ReasoningEffortValues
2829
} from '../../../shared/model/ai-settings';
2930

3031
export const QuestionCondensingDefaultPrompt: string = `You are a helpful assistant that reformulates questions.
@@ -416,7 +417,8 @@ const EnginesConfigurations_Llm: EnginesConfiguration[] = [
416417
{ key: 'apiKey', label: 'Api key', type: 'obfuscated', confirmExport: true },
417418
{ key: 'baseUrl', label: 'Base url', type: 'text', defaultValue: 'https://api.openai.com/v1' },
418419
{ key: 'model', label: 'Model name', type: 'openlist', source: OpenAIModelsList },
419-
{ key: 'temperature', label: 'Temperature', type: 'number', inputScale: 'fullwidth', min: 0, max: 1, step: 0.05 }
420+
{ key: 'temperature', label: 'Temperature', type: 'number', inputScale: 'fullwidth', min: 0, max: 1, step: 0.05 },
421+
{ key: 'reasoningEffort', label: 'Reasoning effort', type: 'openlist', source: ReasoningEffortValues, defaultValue: 'low' }
420422
]
421423
},
422424
{
@@ -428,7 +430,8 @@ const EnginesConfigurations_Llm: EnginesConfiguration[] = [
428430
{ key: 'deploymentName', label: 'Deployment name', type: 'text' },
429431
{ key: 'model', label: 'Model name', type: 'openlist', source: OpenAIModelsList },
430432
{ key: 'apiBase', label: 'Base url', type: 'obfuscated' },
431-
{ key: 'temperature', label: 'Temperature', type: 'number', inputScale: 'fullwidth', min: 0, max: 1, step: 0.05 }
433+
{ key: 'temperature', label: 'Temperature', type: 'number', inputScale: 'fullwidth', min: 0, max: 1, step: 0.05 },
434+
{ key: 'reasoningEffort', label: 'Reasoning effort', type: 'openlist', source: ReasoningEffortValues, defaultValue: 'low' }
432435
]
433436
},
434437
{

bot/admin/web/src/app/shared/model/ai-settings.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export interface llmSetting {
7070
apiVersion?: String;
7171

7272
temperature?: Number;
73+
reasoningEffort?: String;
7374
}
7475

7576
export interface emSetting {
@@ -125,3 +126,6 @@ export const OpenAIEmbeddingModel: string[] = ['text-embedding-3-small', 'text-e
125126
export const OllamaLlmModelsList: string[] = ['llama2', 'llama3', 'llama3.1', 'llama3.1:8b', 'llama3.2'];
126127

127128
export const OllamaEmModelsList: string[] = ['mxbai-embed-large', 'nomic-embed-text', 'all-minilm'];
129+
130+
export const ReasoningEffortValues: string[] = ['low', 'medium', 'high'];
131+

bot/connector-web-sse/src/test/kotlin/ai/tock/bot/connector/web/sse/SseEndpointTest.kt

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@ class SseEndpointTest {
5454
private val path = "/api/bot/sse"
5555
private val connectorId = "test-connector"
5656
private val userId = "integration-test-user"
57-
private val port = 8888
58-
5957
private lateinit var client: HttpClient
6058

6159
@BeforeEach
@@ -65,15 +63,19 @@ class SseEndpointTest {
6563
every { channelDAO.save(any()) } just runs
6664
every { webSecurityHandler.handle(any()) } answers { firstArg<RoutingContext>().next() }
6765

68-
client = vertx.createHttpClient(HttpClientOptions().setDefaultPort(port).setDefaultHost("localhost"))
69-
7066
val router = Router.router(vertx)
7167
endpoint.configureRoute(router, path, connectorId, webSecurityHandler)
7268

7369
vertx.createHttpServer()
7470
.requestHandler(router)
75-
.listen(port)
76-
.onComplete(succeedingThenComplete())
71+
.listen(0)
72+
.onComplete(
73+
succeeding { server ->
74+
val port = server.actualPort()
75+
client = vertx.createHttpClient(HttpClientOptions().setDefaultPort(port).setDefaultHost("localhost"))
76+
completeNow()
77+
},
78+
)
7779
}
7880

7981
@Test

gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/mappers/LLMSettingMapper.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ object LLMSettingMapper {
3939
OpenAILLMSetting(
4040
apiKey = SecurityUtils.fetchSecretKeyValue(apiKey),
4141
temperature = temperature,
42+
reasoningEffort = reasoningEffort,
4243
model = model,
4344
baseUrl = baseUrl,
4445
)
@@ -47,6 +48,7 @@ object LLMSettingMapper {
4748
AzureOpenAILLMSetting(
4849
apiKey = SecurityUtils.fetchSecretKeyValue(apiKey),
4950
temperature = temperature,
51+
reasoningEffort = reasoningEffort,
5052
apiBase = apiBase,
5153
deploymentName = deploymentName,
5254
model = model,
@@ -87,6 +89,7 @@ object LLMSettingMapper {
8789
OpenAILLMSetting(
8890
apiKey = SecurityUtils.createSecretKey(namespace, botId, feature, apiKey, rawByForce),
8991
temperature = temperature,
92+
reasoningEffort = reasoningEffort,
9093
model = model,
9194
baseUrl = baseUrl,
9295
)
@@ -95,6 +98,7 @@ object LLMSettingMapper {
9598
AzureOpenAILLMSetting(
9699
SecurityUtils.createSecretKey(namespace, botId, feature, apiKey, rawByForce),
97100
temperature = temperature,
101+
reasoningEffort = reasoningEffort,
98102
apiBase = apiBase,
99103
deploymentName = deploymentName,
100104
apiVersion = apiVersion,

gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/llm/AzureOpenAILLMSetting.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ package ai.tock.genai.orchestratorcore.models.llm
1919
data class AzureOpenAILLMSetting<T>(
2020
override val apiKey: T,
2121
override val temperature: String,
22+
override val reasoningEffort: String? = null,
2223
@Deprecated("use PromptTemplate#prompt")
2324
override val prompt: String? = null,
2425
val apiBase: String,
2526
val deploymentName: String,
2627
val apiVersion: String,
2728
val model: String? = null,
28-
) : LLMSettingBase<T>(LLMProvider.AzureOpenAIService, apiKey, temperature, prompt) {
29+
) : LLMSettingBase<T>(LLMProvider.AzureOpenAIService, apiKey, temperature, reasoningEffort, prompt) {
2930
override fun copyWithTemperature(temperature: String): LLMSettingBase<T> {
3031
return this.copy(temperature = temperature)
3132
}

gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/llm/LLMSettingBase.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ abstract class LLMSettingBase<T>(
3636
val provider: LLMProvider,
3737
open val apiKey: T? = null,
3838
open val temperature: String,
39+
open val reasoningEffort: String? = null,
3940
@Deprecated("use PromptTemplate#prompt")
4041
open val prompt: String? = null,
4142
) {

gen-ai/orchestrator-core/src/main/kotlin/ai/tock/genai/orchestratorcore/models/llm/OpenAILLMSetting.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,12 @@ package ai.tock.genai.orchestratorcore.models.llm
1919
data class OpenAILLMSetting<T>(
2020
override val apiKey: T,
2121
override val temperature: String,
22+
override val reasoningEffort: String? = null,
2223
@Deprecated("use PromptTemplate#prompt")
2324
override val prompt: String? = null,
2425
val model: String,
2526
val baseUrl: String,
26-
) : LLMSettingBase<T>(LLMProvider.OpenAI, apiKey, temperature, prompt) {
27+
) : LLMSettingBase<T>(LLMProvider.OpenAI, apiKey, temperature, reasoningEffort, prompt) {
2728
override fun copyWithTemperature(temperature: String): LLMSettingBase<T> {
2829
return this.copy(temperature = temperature)
2930
}
Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/*
2+
* Copyright (C) 2017/2025 SNCF Connect & Tech
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package ai.tock.genai.orchestratorcore.mappers
18+
19+
import ai.tock.genai.orchestratorcore.models.llm.AzureOpenAILLMSetting
20+
import ai.tock.genai.orchestratorcore.models.llm.LLMProvider
21+
import ai.tock.genai.orchestratorcore.models.llm.LLMSetting
22+
import ai.tock.genai.orchestratorcore.models.llm.OpenAILLMSetting
23+
import ai.tock.shared.security.key.RawSecretKey
24+
import org.junit.jupiter.api.Test
25+
import kotlin.test.assertEquals
26+
import kotlin.test.assertNull
27+
28+
class LLMSettingMapperReasoningEffortTest {
29+
// Helper to create typed entities for toDTO tests
30+
// Data class generics in Kotlin are invariant, so we need unchecked casts
31+
@Suppress("UNCHECKED_CAST")
32+
private fun openAiEntity(
33+
temperature: String,
34+
model: String,
35+
reasoningEffort: String? = null,
36+
): LLMSetting =
37+
OpenAILLMSetting(
38+
apiKey = RawSecretKey("sk-test"),
39+
temperature = temperature,
40+
reasoningEffort = reasoningEffort,
41+
model = model,
42+
baseUrl = "https://api.openai.com/v1",
43+
) as LLMSetting
44+
45+
@Suppress("UNCHECKED_CAST")
46+
private fun azureEntity(
47+
temperature: String,
48+
reasoningEffort: String? = null,
49+
model: String? = "gpt-5",
50+
): LLMSetting =
51+
AzureOpenAILLMSetting(
52+
apiKey = RawSecretKey("azure-key"),
53+
temperature = temperature,
54+
reasoningEffort = reasoningEffort,
55+
apiBase = "https://endpoint.openai.azure.com",
56+
deploymentName = "deploy",
57+
apiVersion = "2025-04-01-preview",
58+
model = model,
59+
) as LLMSetting
60+
61+
// ---------------------------------------------------------------
62+
// toDTO() tests
63+
// ---------------------------------------------------------------
64+
65+
@Test
66+
fun `toDTO should preserve reasoningEffort for OpenAI`() {
67+
val dto = LLMSettingMapper.toDTO(openAiEntity("0.7", "gpt-5", "high"))
68+
assertEquals(LLMProvider.OpenAI, dto.provider)
69+
assertEquals("sk-test", dto.apiKey)
70+
assertEquals("high", dto.reasoningEffort)
71+
}
72+
73+
@Test
74+
fun `toDTO should preserve null reasoningEffort for OpenAI`() {
75+
val dto = LLMSettingMapper.toDTO(openAiEntity("0.7", "gpt-4o"))
76+
assertNull(dto.reasoningEffort)
77+
}
78+
79+
@Test
80+
fun `toDTO should preserve reasoningEffort for AzureOpenAI`() {
81+
val dto = LLMSettingMapper.toDTO(azureEntity("0.5", "medium"))
82+
assertEquals("medium", dto.reasoningEffort)
83+
assertEquals("deploy", (dto as AzureOpenAILLMSetting).deploymentName)
84+
}
85+
86+
@Test
87+
fun `toDTO should preserve null reasoningEffort for AzureOpenAI`() {
88+
val dto = LLMSettingMapper.toDTO(azureEntity("0.5"))
89+
assertNull(dto.reasoningEffort)
90+
}
91+
92+
// ---------------------------------------------------------------
93+
// toEntity() tests
94+
// ---------------------------------------------------------------
95+
96+
@Test
97+
fun `toEntity should preserve reasoningEffort for OpenAI`() {
98+
val dto =
99+
OpenAILLMSetting(
100+
apiKey = "sk-test",
101+
temperature = "0.7",
102+
reasoningEffort = "high",
103+
model = "gpt-5",
104+
baseUrl = "https://api.openai.com/v1",
105+
)
106+
val entity = LLMSettingMapper.toEntity(dto = dto, rawByForce = true)
107+
assertEquals("high", entity.reasoningEffort)
108+
assertEquals("sk-test", (entity.apiKey as RawSecretKey).secret)
109+
}
110+
111+
@Test
112+
fun `toEntity should preserve null reasoningEffort for OpenAI`() {
113+
val dto =
114+
OpenAILLMSetting(
115+
apiKey = "sk-test",
116+
temperature = "0.7",
117+
model = "gpt-4o",
118+
baseUrl = "https://api.openai.com/v1",
119+
)
120+
val entity = LLMSettingMapper.toEntity(dto = dto, rawByForce = true)
121+
assertNull(entity.reasoningEffort)
122+
}
123+
124+
@Test
125+
fun `toEntity should preserve reasoningEffort for AzureOpenAI`() {
126+
val dto =
127+
AzureOpenAILLMSetting(
128+
apiKey = "azure-key",
129+
temperature = "0.5",
130+
reasoningEffort = "low",
131+
apiBase = "https://endpoint.openai.azure.com",
132+
deploymentName = "deploy",
133+
apiVersion = "2025-04-01-preview",
134+
model = "gpt-5",
135+
)
136+
val entity = LLMSettingMapper.toEntity(dto = dto, rawByForce = true)
137+
assertEquals("low", entity.reasoningEffort)
138+
}
139+
140+
// ---------------------------------------------------------------
141+
// Round-trip tests
142+
// ---------------------------------------------------------------
143+
144+
@Test
145+
fun `round-trip should preserve reasoningEffort for OpenAI`() {
146+
val original =
147+
OpenAILLMSetting(
148+
apiKey = "sk-rt",
149+
temperature = "0.9",
150+
reasoningEffort = "high",
151+
model = "gpt-5",
152+
baseUrl = "https://api.openai.com/v1",
153+
)
154+
val entity = LLMSettingMapper.toEntity(dto = original, rawByForce = true)
155+
val result = LLMSettingMapper.toDTO(entity)
156+
assertEquals(original.reasoningEffort, result.reasoningEffort)
157+
assertEquals(original.apiKey, result.apiKey)
158+
}
159+
160+
@Test
161+
fun `round-trip should preserve reasoningEffort for AzureOpenAI`() {
162+
val original =
163+
AzureOpenAILLMSetting(
164+
apiKey = "azure-rt",
165+
temperature = "0.5",
166+
reasoningEffort = "low",
167+
apiBase = "https://endpoint.openai.azure.com",
168+
deploymentName = "deploy",
169+
apiVersion = "2025-04-01-preview",
170+
model = "gpt-5",
171+
)
172+
val entity = LLMSettingMapper.toEntity(dto = original, rawByForce = true)
173+
val result = LLMSettingMapper.toDTO(entity)
174+
assertEquals(original.reasoningEffort, result.reasoningEffort)
175+
}
176+
177+
@Test
178+
fun `round-trip should preserve null reasoningEffort`() {
179+
val original =
180+
OpenAILLMSetting(
181+
apiKey = "sk-no-re",
182+
temperature = "0.7",
183+
model = "gpt-4o",
184+
baseUrl = "https://api.openai.com/v1",
185+
)
186+
val entity = LLMSettingMapper.toEntity(dto = original, rawByForce = true)
187+
val result = LLMSettingMapper.toDTO(entity)
188+
assertNull(result.reasoningEffort)
189+
}
190+
}

0 commit comments

Comments
 (0)