@lobehub/lobehub 2.0.0-next.190 → 2.0.0-next.191
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/package.json +1 -1
- package/packages/const/src/utils/merge.test.ts +679 -0
- package/packages/context-engine/src/processors/__tests__/AgentCouncilFlatten.test.ts +0 -20
- package/packages/context-engine/src/processors/__tests__/MessageContent.test.ts +5 -23
- package/packages/context-engine/src/providers/SystemRoleInjector.ts +0 -1
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/agentCouncil/simple.json +4 -19
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/agentCouncil/with-supervisor-reply.json +4 -23
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/agentGroup/speak-different-agent.json +3 -13
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/assistant-chain-with-followup.json +3 -8
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/assistantGroup/assistant-with-tools.json +21 -17
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/assistantGroup/tools-with-branches.json +15 -15
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/active-index-1.json +3 -13
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/assistant-branch.json +2 -9
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/assistant-group-branches.json +0 -11
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/assistant-user-branch.json +5 -15
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/conversation.json +4 -14
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/multi-assistant-group.json +0 -13
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/branch/nested.json +2 -15
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/compare/simple.json +4 -9
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/compare/with-tools.json +8 -17
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/linear-conversation.json +3 -7
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/tasks/simple.json +1 -7
- package/packages/conversation-flow/src/__tests__/fixtures/inputs/tasks/with-summary.json +10 -11
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/agentCouncil/simple.json +2 -32
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/agentCouncil/with-supervisor-reply.json +8 -46
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/agentGroup/speak-different-agent.json +5 -24
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/assistant-chain-with-followup.json +5 -13
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/assistantGroup/assistant-with-tools.json +6 -16
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/assistantGroup/tools-with-branches.json +6 -17
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/active-index-1.json +4 -18
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/assistant-branch.json +4 -16
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/assistant-group-branches.json +0 -19
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/assistant-user-branch.json +8 -24
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/conversation.json +7 -23
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/multi-assistant-group.json +0 -15
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/branch/nested.json +4 -25
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/compare/simple.json +2 -13
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/compare/with-tools.json +4 -20
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/linear-conversation.json +4 -12
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/tasks/simple.json +2 -14
- package/packages/conversation-flow/src/__tests__/fixtures/outputs/tasks/with-summary.json +20 -22
- package/packages/conversation-flow/src/__tests__/indexing.test.ts +0 -35
- package/packages/conversation-flow/src/__tests__/structuring.test.ts +0 -41
- package/packages/conversation-flow/src/transformation/FlatListBuilder.ts +0 -4
- package/packages/conversation-flow/src/transformation/__tests__/BranchResolver.test.ts +0 -10
- package/packages/conversation-flow/src/transformation/__tests__/ContextTreeBuilder.test.ts +0 -19
- package/packages/conversation-flow/src/transformation/__tests__/FlatListBuilder.test.ts +0 -37
- package/packages/conversation-flow/src/transformation/__tests__/MessageCollector.test.ts +0 -12
- package/packages/conversation-flow/src/transformation/__tests__/MessageTransformer.test.ts +0 -2
- package/packages/database/src/models/message.ts +0 -1
- package/packages/prompts/src/prompts/chatMessages/index.test.ts +0 -1
- package/packages/prompts/src/prompts/groupChat/__snapshots__/index.test.ts.snap +0 -21
- package/packages/prompts/src/prompts/groupChat/index.test.ts +0 -3
- package/packages/types/src/message/ui/chat.ts +0 -2
- package/src/features/Conversation/store/slices/data/action.test.ts +0 -14
- package/src/features/Conversation/store/slices/data/reducer.test.ts +0 -21
- package/src/features/Conversation/store/slices/data/reducer.ts +1 -1
- package/src/features/Conversation/store/slices/message/action/crud.test.ts +0 -10
- package/src/server/modules/Mecha/ContextEngineering/__tests__/serverMessagesEngine.test.ts +3 -5
- package/src/server/routers/lambda/__tests__/message.test.ts +1 -2
- package/src/server/services/agentRuntime/AgentRuntimeService.ts +109 -109
- package/src/server/services/agentRuntime/types.ts +8 -8
- package/src/server/services/doc/index.tsx +2 -2
- package/src/server/services/generation/index.ts +2 -2
- package/src/server/services/message/index.ts +3 -3
- package/src/server/services/usage/index.ts +4 -4
- package/src/services/chat/chat.test.ts +0 -6
- package/src/services/chat/mecha/contextEngineering.test.ts +3 -29
- package/src/services/chat/mecha/modelParamsResolver.test.ts +803 -0
- package/src/store/chat/agents/GroupOrchestration/createGroupOrchestrationExecutors.ts +0 -2
- package/src/store/chat/agents/__tests__/createAgentExecutors/call-tool.test.ts +0 -6
- package/src/store/chat/agents/__tests__/createAgentExecutors/fixtures/mockMessages.ts +0 -3
- package/src/store/chat/agents/createAgentExecutors.ts +4 -4
- package/src/store/chat/slices/aiAgent/actions/__tests__/agentGroup.test.ts +0 -2
- package/src/store/chat/slices/aiAgent/actions/__tests__/runAgent.test.ts +0 -3
- package/src/store/chat/slices/aiChat/actions/conversationLifecycle.ts +0 -1
- package/src/store/chat/slices/builtinTool/actions/__tests__/search.test.ts +0 -4
- package/src/store/chat/slices/message/reducer.test.ts +0 -5
- package/src/store/chat/slices/message/reducer.ts +1 -1
- package/src/store/chat/slices/message/selectors/displayMessage.test.ts +0 -13
- package/src/store/chat/slices/message/selectors/displayMessage.ts +3 -34
- package/src/store/chat/slices/portal/selectors.test.ts +0 -7
- package/src/store/chat/slices/thread/action.test.ts +0 -1
- package/src/store/chat/slices/translate/action.test.ts +0 -1
- package/src/store/tool/slices/oldStore/action.test.ts +0 -1
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import * as aiInfraStore from '@/store/aiInfra';
|
|
4
|
+
import * as aiModelSelectors from '@/store/aiInfra/slices/aiModel/selectors';
|
|
5
|
+
|
|
6
|
+
import { resolveModelExtendParams } from './modelParamsResolver';
|
|
7
|
+
|
|
8
|
+
describe('resolveModelExtendParams', () => {
|
|
9
|
+
const mockAiInfraStoreState = { someState: true };
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
vi.restoreAllMocks();
|
|
13
|
+
|
|
14
|
+
vi.spyOn(aiInfraStore, 'getAiInfraStoreState').mockReturnValue(mockAiInfraStoreState as any);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
describe('when model has no extend params', () => {
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
20
|
+
() => false,
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should return empty object when model has no extend params support', () => {
|
|
25
|
+
const result = resolveModelExtendParams({
|
|
26
|
+
chatConfig: { enableReasoning: true } as any,
|
|
27
|
+
model: 'gpt-4',
|
|
28
|
+
provider: 'openai',
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
expect(result).toEqual({});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should return empty object even if chatConfig has extended params configured', () => {
|
|
35
|
+
const result = resolveModelExtendParams({
|
|
36
|
+
chatConfig: {
|
|
37
|
+
disableContextCaching: true,
|
|
38
|
+
enableReasoning: true,
|
|
39
|
+
reasoningBudgetToken: 2048,
|
|
40
|
+
reasoningEffort: 'high',
|
|
41
|
+
} as any,
|
|
42
|
+
model: 'basic-model',
|
|
43
|
+
provider: 'provider',
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(result).toEqual({});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe('when model has extend params but no modelExtendParams available', () => {
|
|
51
|
+
beforeEach(() => {
|
|
52
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
53
|
+
() => true,
|
|
54
|
+
);
|
|
55
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(
|
|
56
|
+
() => undefined,
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should return empty object when modelExtendParams is undefined', () => {
|
|
61
|
+
const result = resolveModelExtendParams({
|
|
62
|
+
chatConfig: { enableReasoning: true } as any,
|
|
63
|
+
model: 'gpt-4',
|
|
64
|
+
provider: 'openai',
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
expect(result).toEqual({});
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
describe('reasoning configuration', () => {
|
|
72
|
+
describe('enableReasoning param', () => {
|
|
73
|
+
beforeEach(() => {
|
|
74
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
75
|
+
() => true,
|
|
76
|
+
);
|
|
77
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
78
|
+
'enableReasoning',
|
|
79
|
+
]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should set thinking to enabled with budget when enableReasoning is true', () => {
|
|
83
|
+
const result = resolveModelExtendParams({
|
|
84
|
+
chatConfig: {
|
|
85
|
+
enableReasoning: true,
|
|
86
|
+
reasoningBudgetToken: 2048,
|
|
87
|
+
} as any,
|
|
88
|
+
model: 'gpt-4',
|
|
89
|
+
provider: 'openai',
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
expect(result.thinking).toEqual({
|
|
93
|
+
budget_tokens: 2048,
|
|
94
|
+
type: 'enabled',
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should use default budget token when not specified', () => {
|
|
99
|
+
const result = resolveModelExtendParams({
|
|
100
|
+
chatConfig: {
|
|
101
|
+
enableReasoning: true,
|
|
102
|
+
} as any,
|
|
103
|
+
model: 'gpt-4',
|
|
104
|
+
provider: 'openai',
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(result.thinking).toEqual({
|
|
108
|
+
budget_tokens: 1024,
|
|
109
|
+
type: 'enabled',
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should set thinking to disabled when enableReasoning is false', () => {
|
|
114
|
+
const result = resolveModelExtendParams({
|
|
115
|
+
chatConfig: {
|
|
116
|
+
enableReasoning: false,
|
|
117
|
+
} as any,
|
|
118
|
+
model: 'gpt-4',
|
|
119
|
+
provider: 'openai',
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(result.thinking).toEqual({
|
|
123
|
+
budget_tokens: 0,
|
|
124
|
+
type: 'disabled',
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
describe('reasoningBudgetToken only param', () => {
|
|
130
|
+
beforeEach(() => {
|
|
131
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
132
|
+
() => true,
|
|
133
|
+
);
|
|
134
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
135
|
+
'reasoningBudgetToken',
|
|
136
|
+
]);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should set thinking to enabled when only reasoningBudgetToken is supported', () => {
|
|
140
|
+
const result = resolveModelExtendParams({
|
|
141
|
+
chatConfig: {
|
|
142
|
+
reasoningBudgetToken: 4096,
|
|
143
|
+
} as any,
|
|
144
|
+
model: 'claude-3',
|
|
145
|
+
provider: 'anthropic',
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
expect(result.thinking).toEqual({
|
|
149
|
+
budget_tokens: 4096,
|
|
150
|
+
type: 'enabled',
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should use default budget when reasoningBudgetToken is not provided', () => {
|
|
155
|
+
const result = resolveModelExtendParams({
|
|
156
|
+
chatConfig: {} as any,
|
|
157
|
+
model: 'claude-3',
|
|
158
|
+
provider: 'anthropic',
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expect(result.thinking).toEqual({
|
|
162
|
+
budget_tokens: 1024,
|
|
163
|
+
type: 'enabled',
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('context caching', () => {
|
|
170
|
+
beforeEach(() => {
|
|
171
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
172
|
+
() => true,
|
|
173
|
+
);
|
|
174
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
175
|
+
'disableContextCaching',
|
|
176
|
+
]);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should set enabledContextCaching to false when disableContextCaching is true', () => {
|
|
180
|
+
const result = resolveModelExtendParams({
|
|
181
|
+
chatConfig: {
|
|
182
|
+
disableContextCaching: true,
|
|
183
|
+
} as any,
|
|
184
|
+
model: 'gpt-4',
|
|
185
|
+
provider: 'openai',
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
expect(result.enabledContextCaching).toBe(false);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should not set enabledContextCaching when disableContextCaching is false', () => {
|
|
192
|
+
const result = resolveModelExtendParams({
|
|
193
|
+
chatConfig: {
|
|
194
|
+
disableContextCaching: false,
|
|
195
|
+
} as any,
|
|
196
|
+
model: 'gpt-4',
|
|
197
|
+
provider: 'openai',
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
expect(result.enabledContextCaching).toBeUndefined();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should not set enabledContextCaching when disableContextCaching is not provided', () => {
|
|
204
|
+
const result = resolveModelExtendParams({
|
|
205
|
+
chatConfig: {} as any,
|
|
206
|
+
model: 'gpt-4',
|
|
207
|
+
provider: 'openai',
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
expect(result.enabledContextCaching).toBeUndefined();
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('reasoning effort variants', () => {
|
|
215
|
+
describe('reasoningEffort param', () => {
|
|
216
|
+
beforeEach(() => {
|
|
217
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
218
|
+
() => true,
|
|
219
|
+
);
|
|
220
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
221
|
+
'reasoningEffort',
|
|
222
|
+
]);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should set reasoning_effort when supported and configured', () => {
|
|
226
|
+
const result = resolveModelExtendParams({
|
|
227
|
+
chatConfig: {
|
|
228
|
+
reasoningEffort: 'medium',
|
|
229
|
+
} as any,
|
|
230
|
+
model: 'gpt-4',
|
|
231
|
+
provider: 'openai',
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
expect(result.reasoning_effort).toBe('medium');
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('should not set reasoning_effort when not configured', () => {
|
|
238
|
+
const result = resolveModelExtendParams({
|
|
239
|
+
chatConfig: {} as any,
|
|
240
|
+
model: 'gpt-4',
|
|
241
|
+
provider: 'openai',
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
expect(result.reasoning_effort).toBeUndefined();
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
describe('gpt5ReasoningEffort param', () => {
|
|
249
|
+
beforeEach(() => {
|
|
250
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
251
|
+
() => true,
|
|
252
|
+
);
|
|
253
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
254
|
+
'gpt5ReasoningEffort',
|
|
255
|
+
]);
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('should set reasoning_effort for gpt5 variant', () => {
|
|
259
|
+
const result = resolveModelExtendParams({
|
|
260
|
+
chatConfig: {
|
|
261
|
+
gpt5ReasoningEffort: 'high',
|
|
262
|
+
} as any,
|
|
263
|
+
model: 'gpt-5',
|
|
264
|
+
provider: 'openai',
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
expect(result.reasoning_effort).toBe('high');
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
describe('gpt5_1ReasoningEffort param', () => {
|
|
272
|
+
beforeEach(() => {
|
|
273
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
274
|
+
() => true,
|
|
275
|
+
);
|
|
276
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
277
|
+
'gpt5_1ReasoningEffort',
|
|
278
|
+
]);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('should set reasoning_effort for gpt5.1 variant', () => {
|
|
282
|
+
const result = resolveModelExtendParams({
|
|
283
|
+
chatConfig: {
|
|
284
|
+
gpt5_1ReasoningEffort: 'low',
|
|
285
|
+
} as any,
|
|
286
|
+
model: 'gpt-5.1',
|
|
287
|
+
provider: 'openai',
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
expect(result.reasoning_effort).toBe('low');
|
|
291
|
+
});
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
describe('gpt5_2ReasoningEffort param', () => {
|
|
295
|
+
beforeEach(() => {
|
|
296
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
297
|
+
() => true,
|
|
298
|
+
);
|
|
299
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
300
|
+
'gpt5_2ReasoningEffort',
|
|
301
|
+
]);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('should set reasoning_effort for gpt5.2 variant', () => {
|
|
305
|
+
const result = resolveModelExtendParams({
|
|
306
|
+
chatConfig: {
|
|
307
|
+
gpt5_2ReasoningEffort: 'medium',
|
|
308
|
+
} as any,
|
|
309
|
+
model: 'gpt-5.2',
|
|
310
|
+
provider: 'openai',
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
expect(result.reasoning_effort).toBe('medium');
|
|
314
|
+
});
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
describe('gpt5_2ProReasoningEffort param', () => {
|
|
318
|
+
beforeEach(() => {
|
|
319
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
320
|
+
() => true,
|
|
321
|
+
);
|
|
322
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
323
|
+
'gpt5_2ProReasoningEffort',
|
|
324
|
+
]);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
it('should set reasoning_effort for gpt5.2-pro variant', () => {
|
|
328
|
+
const result = resolveModelExtendParams({
|
|
329
|
+
chatConfig: {
|
|
330
|
+
gpt5_2ProReasoningEffort: 'high',
|
|
331
|
+
} as any,
|
|
332
|
+
model: 'gpt-5.2-pro',
|
|
333
|
+
provider: 'openai',
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
expect(result.reasoning_effort).toBe('high');
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
describe('text verbosity', () => {
|
|
342
|
+
beforeEach(() => {
|
|
343
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
344
|
+
() => true,
|
|
345
|
+
);
|
|
346
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
347
|
+
'textVerbosity',
|
|
348
|
+
]);
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
it('should set verbosity when textVerbosity is supported and configured', () => {
|
|
352
|
+
const result = resolveModelExtendParams({
|
|
353
|
+
chatConfig: {
|
|
354
|
+
textVerbosity: 'detailed',
|
|
355
|
+
} as any,
|
|
356
|
+
model: 'model',
|
|
357
|
+
provider: 'provider',
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
expect(result.verbosity).toBe('detailed');
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('should not set verbosity when textVerbosity is not configured', () => {
|
|
364
|
+
const result = resolveModelExtendParams({
|
|
365
|
+
chatConfig: {} as any,
|
|
366
|
+
model: 'model',
|
|
367
|
+
provider: 'provider',
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
expect(result.verbosity).toBeUndefined();
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
describe('thinking configuration', () => {
|
|
375
|
+
describe('thinking param', () => {
|
|
376
|
+
beforeEach(() => {
|
|
377
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
378
|
+
() => true,
|
|
379
|
+
);
|
|
380
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
381
|
+
'thinking',
|
|
382
|
+
]);
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('should set thinking type when supported and configured', () => {
|
|
386
|
+
const result = resolveModelExtendParams({
|
|
387
|
+
chatConfig: {
|
|
388
|
+
thinking: 'extended',
|
|
389
|
+
} as any,
|
|
390
|
+
model: 'deepseek',
|
|
391
|
+
provider: 'deepseek',
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
expect(result.thinking).toEqual({
|
|
395
|
+
type: 'extended',
|
|
396
|
+
});
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
it('should not set thinking when not configured', () => {
|
|
400
|
+
const result = resolveModelExtendParams({
|
|
401
|
+
chatConfig: {} as any,
|
|
402
|
+
model: 'deepseek',
|
|
403
|
+
provider: 'deepseek',
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
expect(result.thinking).toBeUndefined();
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
describe('thinkingBudget param', () => {
|
|
411
|
+
beforeEach(() => {
|
|
412
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
413
|
+
() => true,
|
|
414
|
+
);
|
|
415
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
416
|
+
'thinkingBudget',
|
|
417
|
+
]);
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('should set thinkingBudget when supported and configured with value', () => {
|
|
421
|
+
const result = resolveModelExtendParams({
|
|
422
|
+
chatConfig: {
|
|
423
|
+
thinkingBudget: 5000,
|
|
424
|
+
} as any,
|
|
425
|
+
model: 'model',
|
|
426
|
+
provider: 'provider',
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
expect(result.thinkingBudget).toBe(5000);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
it('should set thinkingBudget to 0 when explicitly set to 0', () => {
|
|
433
|
+
const result = resolveModelExtendParams({
|
|
434
|
+
chatConfig: {
|
|
435
|
+
thinkingBudget: 0,
|
|
436
|
+
} as any,
|
|
437
|
+
model: 'model',
|
|
438
|
+
provider: 'provider',
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
expect(result.thinkingBudget).toBe(0);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
it('should not set thinkingBudget when undefined', () => {
|
|
445
|
+
const result = resolveModelExtendParams({
|
|
446
|
+
chatConfig: {} as any,
|
|
447
|
+
model: 'model',
|
|
448
|
+
provider: 'provider',
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
expect(result.thinkingBudget).toBeUndefined();
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
describe('thinkingLevel param', () => {
|
|
456
|
+
beforeEach(() => {
|
|
457
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
458
|
+
() => true,
|
|
459
|
+
);
|
|
460
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
461
|
+
'thinkingLevel',
|
|
462
|
+
]);
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
it('should set thinkingLevel when supported and configured', () => {
|
|
466
|
+
const result = resolveModelExtendParams({
|
|
467
|
+
chatConfig: {
|
|
468
|
+
thinkingLevel: 'advanced',
|
|
469
|
+
} as any,
|
|
470
|
+
model: 'model',
|
|
471
|
+
provider: 'provider',
|
|
472
|
+
});
|
|
473
|
+
|
|
474
|
+
expect(result.thinkingLevel).toBe('advanced');
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
it('should not set thinkingLevel when not configured', () => {
|
|
478
|
+
const result = resolveModelExtendParams({
|
|
479
|
+
chatConfig: {} as any,
|
|
480
|
+
model: 'model',
|
|
481
|
+
provider: 'provider',
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
expect(result.thinkingLevel).toBeUndefined();
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
describe('URL context', () => {
|
|
490
|
+
beforeEach(() => {
|
|
491
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
492
|
+
() => true,
|
|
493
|
+
);
|
|
494
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
495
|
+
'urlContext',
|
|
496
|
+
]);
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
it('should set urlContext when supported and enabled', () => {
|
|
500
|
+
const result = resolveModelExtendParams({
|
|
501
|
+
chatConfig: {
|
|
502
|
+
urlContext: true,
|
|
503
|
+
} as any,
|
|
504
|
+
model: 'model',
|
|
505
|
+
provider: 'provider',
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
expect(result.urlContext).toBe(true);
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it('should not set urlContext when false', () => {
|
|
512
|
+
const result = resolveModelExtendParams({
|
|
513
|
+
chatConfig: {
|
|
514
|
+
urlContext: false,
|
|
515
|
+
} as any,
|
|
516
|
+
model: 'model',
|
|
517
|
+
provider: 'provider',
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
expect(result.urlContext).toBeUndefined();
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
it('should not set urlContext when not configured', () => {
|
|
524
|
+
const result = resolveModelExtendParams({
|
|
525
|
+
chatConfig: {} as any,
|
|
526
|
+
model: 'model',
|
|
527
|
+
provider: 'provider',
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
expect(result.urlContext).toBeUndefined();
|
|
531
|
+
});
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
describe('image generation params', () => {
|
|
535
|
+
describe('imageAspectRatio param', () => {
|
|
536
|
+
beforeEach(() => {
|
|
537
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
538
|
+
() => true,
|
|
539
|
+
);
|
|
540
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
541
|
+
'imageAspectRatio',
|
|
542
|
+
]);
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it('should set imageAspectRatio when supported and configured', () => {
|
|
546
|
+
const result = resolveModelExtendParams({
|
|
547
|
+
chatConfig: {
|
|
548
|
+
imageAspectRatio: '16:9',
|
|
549
|
+
} as any,
|
|
550
|
+
model: 'dall-e-3',
|
|
551
|
+
provider: 'openai',
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
expect(result.imageAspectRatio).toBe('16:9');
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
it('should not set imageAspectRatio when not configured', () => {
|
|
558
|
+
const result = resolveModelExtendParams({
|
|
559
|
+
chatConfig: {} as any,
|
|
560
|
+
model: 'dall-e-3',
|
|
561
|
+
provider: 'openai',
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
expect(result.imageAspectRatio).toBeUndefined();
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
|
|
568
|
+
describe('imageResolution param', () => {
|
|
569
|
+
beforeEach(() => {
|
|
570
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
571
|
+
() => true,
|
|
572
|
+
);
|
|
573
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
574
|
+
'imageResolution',
|
|
575
|
+
]);
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
it('should set imageResolution when supported and configured', () => {
|
|
579
|
+
const result = resolveModelExtendParams({
|
|
580
|
+
chatConfig: {
|
|
581
|
+
imageResolution: '1024x1024',
|
|
582
|
+
} as any,
|
|
583
|
+
model: 'dall-e-3',
|
|
584
|
+
provider: 'openai',
|
|
585
|
+
});
|
|
586
|
+
|
|
587
|
+
expect(result.imageResolution).toBe('1024x1024');
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
it('should not set imageResolution when not configured', () => {
|
|
591
|
+
const result = resolveModelExtendParams({
|
|
592
|
+
chatConfig: {} as any,
|
|
593
|
+
model: 'dall-e-3',
|
|
594
|
+
provider: 'openai',
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
expect(result.imageResolution).toBeUndefined();
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
describe('multiple params combination', () => {
|
|
603
|
+
beforeEach(() => {
|
|
604
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
605
|
+
() => true,
|
|
606
|
+
);
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it('should handle multiple params together correctly', () => {
|
|
610
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
611
|
+
'enableReasoning',
|
|
612
|
+
'reasoningEffort',
|
|
613
|
+
'textVerbosity',
|
|
614
|
+
'urlContext',
|
|
615
|
+
'disableContextCaching',
|
|
616
|
+
]);
|
|
617
|
+
|
|
618
|
+
const result = resolveModelExtendParams({
|
|
619
|
+
chatConfig: {
|
|
620
|
+
disableContextCaching: true,
|
|
621
|
+
enableReasoning: true,
|
|
622
|
+
reasoningBudgetToken: 3072,
|
|
623
|
+
reasoningEffort: 'high',
|
|
624
|
+
textVerbosity: 'concise',
|
|
625
|
+
urlContext: true,
|
|
626
|
+
} as any,
|
|
627
|
+
model: 'gpt-4',
|
|
628
|
+
provider: 'openai',
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
expect(result).toEqual({
|
|
632
|
+
enabledContextCaching: false,
|
|
633
|
+
reasoning_effort: 'high',
|
|
634
|
+
thinking: {
|
|
635
|
+
budget_tokens: 3072,
|
|
636
|
+
type: 'enabled',
|
|
637
|
+
},
|
|
638
|
+
urlContext: true,
|
|
639
|
+
verbosity: 'concise',
|
|
640
|
+
});
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
it('should only set params that are both supported and configured', () => {
|
|
644
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
645
|
+
'enableReasoning',
|
|
646
|
+
'textVerbosity',
|
|
647
|
+
]);
|
|
648
|
+
|
|
649
|
+
const result = resolveModelExtendParams({
|
|
650
|
+
chatConfig: {
|
|
651
|
+
enableReasoning: true,
|
|
652
|
+
imageAspectRatio: '1:1', // Not supported
|
|
653
|
+
reasoningBudgetToken: 2048,
|
|
654
|
+
textVerbosity: 'detailed',
|
|
655
|
+
urlContext: true, // Not supported
|
|
656
|
+
} as any,
|
|
657
|
+
model: 'model',
|
|
658
|
+
provider: 'provider',
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
expect(result).toEqual({
|
|
662
|
+
thinking: {
|
|
663
|
+
budget_tokens: 2048,
|
|
664
|
+
type: 'enabled',
|
|
665
|
+
},
|
|
666
|
+
verbosity: 'detailed',
|
|
667
|
+
});
|
|
668
|
+
expect(result.imageAspectRatio).toBeUndefined();
|
|
669
|
+
expect(result.urlContext).toBeUndefined();
|
|
670
|
+
});
|
|
671
|
+
|
|
672
|
+
it('should handle image generation params together', () => {
|
|
673
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
674
|
+
'imageAspectRatio',
|
|
675
|
+
'imageResolution',
|
|
676
|
+
]);
|
|
677
|
+
|
|
678
|
+
const result = resolveModelExtendParams({
|
|
679
|
+
chatConfig: {
|
|
680
|
+
imageAspectRatio: '4:3',
|
|
681
|
+
imageResolution: '2048x2048',
|
|
682
|
+
} as any,
|
|
683
|
+
model: 'dall-e-3',
|
|
684
|
+
provider: 'openai',
|
|
685
|
+
});
|
|
686
|
+
|
|
687
|
+
expect(result).toEqual({
|
|
688
|
+
imageAspectRatio: '4:3',
|
|
689
|
+
imageResolution: '2048x2048',
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
|
|
693
|
+
it('should handle all thinking-related params together', () => {
|
|
694
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
695
|
+
'thinking',
|
|
696
|
+
'thinkingBudget',
|
|
697
|
+
'thinkingLevel',
|
|
698
|
+
]);
|
|
699
|
+
|
|
700
|
+
const result = resolveModelExtendParams({
|
|
701
|
+
chatConfig: {
|
|
702
|
+
thinking: 'enabled',
|
|
703
|
+
thinkingBudget: 8000,
|
|
704
|
+
thinkingLevel: 'expert',
|
|
705
|
+
} as any,
|
|
706
|
+
model: 'deepseek',
|
|
707
|
+
provider: 'deepseek',
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
expect(result).toEqual({
|
|
711
|
+
thinking: {
|
|
712
|
+
type: 'enabled',
|
|
713
|
+
},
|
|
714
|
+
thinkingBudget: 8000,
|
|
715
|
+
thinkingLevel: 'expert',
|
|
716
|
+
});
|
|
717
|
+
});
|
|
718
|
+
});
|
|
719
|
+
|
|
720
|
+
describe('edge cases', () => {
|
|
721
|
+
beforeEach(() => {
|
|
722
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams').mockReturnValue(
|
|
723
|
+
() => true,
|
|
724
|
+
);
|
|
725
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => [
|
|
726
|
+
'enableReasoning',
|
|
727
|
+
'textVerbosity',
|
|
728
|
+
'urlContext',
|
|
729
|
+
'thinkingBudget',
|
|
730
|
+
]);
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
it('should handle empty chatConfig', () => {
|
|
734
|
+
const result = resolveModelExtendParams({
|
|
735
|
+
chatConfig: {} as any,
|
|
736
|
+
model: 'model',
|
|
737
|
+
provider: 'provider',
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
// enableReasoning defaults to false/undefined, so thinking is set to disabled
|
|
741
|
+
expect(result.thinking).toEqual({
|
|
742
|
+
budget_tokens: 0,
|
|
743
|
+
type: 'disabled',
|
|
744
|
+
});
|
|
745
|
+
expect(result.verbosity).toBeUndefined();
|
|
746
|
+
expect(result.urlContext).toBeUndefined();
|
|
747
|
+
expect(result.thinkingBudget).toBeUndefined();
|
|
748
|
+
});
|
|
749
|
+
|
|
750
|
+
it('should handle null/undefined chatConfig values gracefully', () => {
|
|
751
|
+
const result = resolveModelExtendParams({
|
|
752
|
+
chatConfig: {
|
|
753
|
+
enableReasoning: undefined,
|
|
754
|
+
textVerbosity: null as any,
|
|
755
|
+
urlContext: undefined,
|
|
756
|
+
} as any,
|
|
757
|
+
model: 'model',
|
|
758
|
+
provider: 'provider',
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
// enableReasoning is undefined (falsy), so thinking is set to disabled
|
|
762
|
+
expect(result).toEqual({
|
|
763
|
+
thinking: {
|
|
764
|
+
budget_tokens: 0,
|
|
765
|
+
type: 'disabled',
|
|
766
|
+
},
|
|
767
|
+
});
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
it('should handle empty modelExtendParams array', () => {
|
|
771
|
+
vi.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams').mockReturnValue(() => []);
|
|
772
|
+
|
|
773
|
+
const result = resolveModelExtendParams({
|
|
774
|
+
chatConfig: {
|
|
775
|
+
enableReasoning: true,
|
|
776
|
+
textVerbosity: 'detailed',
|
|
777
|
+
} as any,
|
|
778
|
+
model: 'model',
|
|
779
|
+
provider: 'provider',
|
|
780
|
+
});
|
|
781
|
+
|
|
782
|
+
expect(result).toEqual({});
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
it('should verify selectors are called with correct parameters', () => {
|
|
786
|
+
const isModelHasExtendParamsSpy = vi
|
|
787
|
+
.spyOn(aiModelSelectors.aiModelSelectors, 'isModelHasExtendParams')
|
|
788
|
+
.mockReturnValue(() => true);
|
|
789
|
+
const modelExtendParamsSpy = vi
|
|
790
|
+
.spyOn(aiModelSelectors.aiModelSelectors, 'modelExtendParams')
|
|
791
|
+
.mockReturnValue(() => ['enableReasoning']);
|
|
792
|
+
|
|
793
|
+
resolveModelExtendParams({
|
|
794
|
+
chatConfig: {} as any,
|
|
795
|
+
model: 'test-model',
|
|
796
|
+
provider: 'test-provider',
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
expect(isModelHasExtendParamsSpy).toHaveBeenCalledWith('test-model', 'test-provider');
|
|
800
|
+
expect(modelExtendParamsSpy).toHaveBeenCalledWith('test-model', 'test-provider');
|
|
801
|
+
});
|
|
802
|
+
});
|
|
803
|
+
});
|