mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-08-01 17:39:55 +08:00
feat(server): update model list (#15375)
This commit is contained in:
@@ -282,43 +282,6 @@ Generated by [AVA](https://avajs.dev).
|
||||
},
|
||||
]
|
||||
|
||||
## should be able to run image executor
|
||||
|
||||
> should generate image stream
|
||||
|
||||
[
|
||||
{
|
||||
params: {
|
||||
key: [
|
||||
'https://example.com/test-image.jpg',
|
||||
'tag1, tag2, tag3, tag4, tag5, ',
|
||||
],
|
||||
},
|
||||
type: 2,
|
||||
},
|
||||
]
|
||||
|
||||
> should render the prompt with params array
|
||||
|
||||
[
|
||||
{
|
||||
modelId: 'test-image',
|
||||
},
|
||||
[
|
||||
{
|
||||
content: 'tag1, tag2, tag3, tag4, tag5, ',
|
||||
params: {
|
||||
tags: [
|
||||
'tag4',
|
||||
'tag5',
|
||||
],
|
||||
},
|
||||
role: 'user',
|
||||
},
|
||||
],
|
||||
{},
|
||||
]
|
||||
|
||||
## should handle generateSessionTitle correctly under various conditions
|
||||
|
||||
> should generate title when conditions are met
|
||||
@@ -437,66 +400,32 @@ Generated by [AVA](https://avajs.dev).
|
||||
|
||||
> should honor requested pro model
|
||||
|
||||
'gemini-2.5-pro'
|
||||
'gpt-5.6-terra'
|
||||
|
||||
> should fallback to default model
|
||||
|
||||
'gemini-2.5-flash'
|
||||
'gpt-5.6-luna'
|
||||
|
||||
> should fallback to default model when requesting pro model during trialing
|
||||
|
||||
'gemini-2.5-flash'
|
||||
'gpt-5.6-luna'
|
||||
|
||||
> should honor requested non-pro model during trialing
|
||||
|
||||
'gemini-2.5-flash'
|
||||
'gpt-5.6-luna'
|
||||
|
||||
> should pick default model when no requested model during trialing
|
||||
|
||||
'gemini-2.5-flash'
|
||||
'gpt-5.6-luna'
|
||||
|
||||
> should pick default model when no requested model during active
|
||||
|
||||
'gemini-2.5-flash'
|
||||
'gpt-5.6-luna'
|
||||
|
||||
> should honor requested pro model during active
|
||||
|
||||
'claude-sonnet-4-5@20250929'
|
||||
'claude-sonnet-4-6'
|
||||
|
||||
> should fallback to default model when requesting non-optional model during active
|
||||
|
||||
'gemini-2.5-flash'
|
||||
|
||||
## should resolve model correctly based on subscription status and prompt config
|
||||
|
||||
> should honor requested pro model
|
||||
|
||||
'gemini-2.5-pro'
|
||||
|
||||
> should fallback to default model
|
||||
|
||||
'gemini-2.5-flash'
|
||||
|
||||
> should fallback to default model when requesting pro model during trialing
|
||||
|
||||
'gemini-2.5-flash'
|
||||
|
||||
> should honor requested non-pro model during trialing
|
||||
|
||||
'gemini-2.5-flash'
|
||||
|
||||
> should pick default model when no requested model during trialing
|
||||
|
||||
'gemini-2.5-flash'
|
||||
|
||||
> should pick default model when no requested model during active
|
||||
|
||||
'gemini-2.5-flash'
|
||||
|
||||
> should honor requested pro model during active
|
||||
|
||||
'claude-sonnet-4-5@20250929'
|
||||
|
||||
> should fallback to default model when requesting non-optional model during active
|
||||
|
||||
'gemini-2.5-flash'
|
||||
'gpt-5.6-luna'
|
||||
|
||||
Binary file not shown.
+1
-1
@@ -364,7 +364,7 @@ Generated by [AVA](https://avajs.dev).
|
||||
'tool_schema_rewrite',
|
||||
],
|
||||
},
|
||||
model: 'gemini-2.5-flash',
|
||||
model: 'gemini-3.6-flash',
|
||||
responseMimeType: 'application/json',
|
||||
schema: {
|
||||
additionalProperties: false,
|
||||
|
||||
BIN
Binary file not shown.
+2
-2
@@ -71,14 +71,14 @@ Generated by [AVA](https://avajs.dev).
|
||||
|
||||
{
|
||||
matchedOptionalModel: true,
|
||||
selectedModel: 'openai-default/gemini-2.5-pro',
|
||||
selectedModel: 'openai-default/gpt-5.6-terra',
|
||||
}
|
||||
|
||||
> prefixed optional miss
|
||||
|
||||
{
|
||||
matchedOptionalModel: false,
|
||||
selectedModel: 'gemini-2.5-flash',
|
||||
selectedModel: 'gpt-5.6-luna',
|
||||
}
|
||||
|
||||
## ExecutionPlan should serialize routed request state and reject host-only signal
|
||||
|
||||
BIN
Binary file not shown.
@@ -441,7 +441,7 @@ The term **“CRDT”** was first introduced by Marc Shapiro, Nuno Preguiça, Ca
|
||||
messages: singleUserPromptMessages(
|
||||
'In one short sentence, explain what AFFiNE AI is and mention AFFiNE by name.'
|
||||
),
|
||||
config: { model: 'gemini-2.5-flash' },
|
||||
config: { model: 'gemini-3.6-flash' },
|
||||
verifier: (t: ExecutionContext<Tester>, result: string) => {
|
||||
assertNotWrappedInCodeBlock(t, result);
|
||||
t.assert(
|
||||
@@ -458,7 +458,7 @@ The term **“CRDT”** was first introduced by Marc Shapiro, Nuno Preguiça, Ca
|
||||
messages: singleUserPromptMessages(
|
||||
'Respond with one short sentence about AFFiNE AI and mention AFFiNE by name.'
|
||||
),
|
||||
config: { model: 'gemini-2.5-flash' },
|
||||
config: { model: 'gemini-3.6-flash' },
|
||||
verifier: (t: ExecutionContext<Tester>, result: string) => {
|
||||
t.truthy(checkStreamObjects(result), 'should be valid stream objects');
|
||||
const assembledText = getStreamObjectText(result);
|
||||
@@ -985,19 +985,19 @@ const TRANSCRIPT_AUDIO_CASES = [
|
||||
name: 'short audio',
|
||||
url: 'https://cdn.affine.pro/copilot-test/MP9qDGuYgnY+ILoEAmHpp3h9Npuw2403EAYMEA.mp3',
|
||||
mimeType: 'audio/mpeg',
|
||||
modelId: 'gemini-2.5-flash',
|
||||
modelId: 'gemini-3.5-flash-lite',
|
||||
},
|
||||
{
|
||||
name: 'middle audio',
|
||||
url: 'https://cdn.affine.pro/copilot-test/2ed05eo1KvZ2tWB_BAjFo67EAPZZY-w4LylUAw.m4a',
|
||||
mimeType: 'audio/m4a',
|
||||
modelId: 'gemini-2.5-flash',
|
||||
modelId: 'gemini-3.5-flash-lite',
|
||||
},
|
||||
{
|
||||
name: 'long audio',
|
||||
url: 'https://cdn.affine.pro/copilot-test/nC9-e7P85PPI2rU29QWwf8slBNRMy92teLIIMw.opus',
|
||||
mimeType: 'audio/opus',
|
||||
modelId: 'gemini-2.5-pro',
|
||||
modelId: 'gemini-3.6-flash',
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@@ -588,7 +588,7 @@ test('should be able to chat with api', async t => {
|
||||
t.is(
|
||||
array2sse(sse2array(ret3).filter(e => e.event !== 'event')),
|
||||
textToEventStream(
|
||||
['https://example.com/gpt-image-1.jpg'],
|
||||
['https://example.com/gpt-image-2.jpg'],
|
||||
messageId,
|
||||
'attachment'
|
||||
),
|
||||
@@ -717,14 +717,13 @@ test('should map action stream preparation errors to SSE error events', async t
|
||||
});
|
||||
|
||||
test('should be able to chat with special image model', async t => {
|
||||
const { app, prompt, storage } = t.context;
|
||||
const { app, storage } = t.context;
|
||||
|
||||
Sinon.stub(storage, 'handleRemoteLink').resolvesArg(2);
|
||||
|
||||
const { id } = await createWorkspace(app);
|
||||
|
||||
const testWithModel = async (promptName: string, finalPrompt: string) => {
|
||||
const model = (await prompt.get(promptName))?.model;
|
||||
const sessionId = await createCopilotSession(
|
||||
app,
|
||||
id,
|
||||
@@ -739,7 +738,7 @@ test('should be able to chat with special image model', async t => {
|
||||
ret3,
|
||||
textToEventStream(
|
||||
[
|
||||
`https://example.com/${model}.jpg`,
|
||||
'https://example.com/gpt-image-2.jpg',
|
||||
`https://example.com/generated/${encodeURIComponent(finalPrompt)}.jpg`,
|
||||
],
|
||||
messageId,
|
||||
@@ -1915,7 +1914,7 @@ test('should be able to transcript', async t => {
|
||||
const route = input.input?.preparedRoutes?.transcribe?.[0] ?? {};
|
||||
const result = buildTranscriptActionResult(
|
||||
route,
|
||||
'gemini-2.5-flash',
|
||||
'gemini-3.5-flash-lite',
|
||||
input.input ?? {}
|
||||
);
|
||||
const actionVersion = input.recipeVersion ?? 'v1';
|
||||
|
||||
@@ -2414,63 +2414,47 @@ test('model selection policy should resolve requested optional models consistent
|
||||
|
||||
t.deepEqual(
|
||||
modelSelection.resolveRequestedModel({
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
requestedModelId: 'gemini-2.5-pro',
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
requestedModelId: 'gpt-5.6-terra',
|
||||
}),
|
||||
{
|
||||
selectedModel: 'gemini-2.5-pro',
|
||||
selectedModel: 'gpt-5.6-terra',
|
||||
matchedOptionalModel: true,
|
||||
}
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
modelSelection.resolveRequestedModel({
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
requestedModelId: 'openai-default/gemini-2.5-pro',
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
requestedModelId: 'openai-default/gpt-5.6-terra',
|
||||
}),
|
||||
{
|
||||
selectedModel: 'openai-default/gemini-2.5-pro',
|
||||
selectedModel: 'openai-default/gpt-5.6-terra',
|
||||
matchedOptionalModel: true,
|
||||
}
|
||||
);
|
||||
|
||||
t.deepEqual(
|
||||
modelSelection.resolveRequestedModel({
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
requestedModelId: 'not-in-optional',
|
||||
}),
|
||||
{
|
||||
selectedModel: 'gemini-2.5-flash',
|
||||
selectedModel: 'gpt-5.6-luna',
|
||||
matchedOptionalModel: false,
|
||||
}
|
||||
);
|
||||
|
||||
t.is(
|
||||
modelSelection.resolveRequestedModel({
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
requestedModelId: 'not-in-optional',
|
||||
}).selectedModel,
|
||||
'gemini-2.5-flash'
|
||||
'gpt-5.6-luna'
|
||||
);
|
||||
});
|
||||
|
||||
@@ -2494,45 +2478,33 @@ test('capability policy host should gate pro model requests by subscription stat
|
||||
{
|
||||
const model1 = await capabilityPolicy.resolveChatModel({
|
||||
userId,
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
proModels: ['gemini-2.5-pro', 'claude-sonnet-4-5@20250929'],
|
||||
requestedModelId: 'gemini-2.5-pro',
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
proModels: ['gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
requestedModelId: 'gpt-5.6-terra',
|
||||
paymentEnabled: false,
|
||||
});
|
||||
t.snapshot(model1, 'should honor requested pro model');
|
||||
|
||||
const model1WithPrefix = await capabilityPolicy.resolveChatModel({
|
||||
userId,
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
proModels: ['gemini-2.5-pro', 'claude-sonnet-4-5@20250929'],
|
||||
requestedModelId: 'openai-default/gemini-2.5-pro',
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
proModels: ['gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
requestedModelId: 'openai-default/gpt-5.6-terra',
|
||||
paymentEnabled: false,
|
||||
});
|
||||
t.is(
|
||||
model1WithPrefix,
|
||||
'openai-default/gemini-2.5-pro',
|
||||
'openai-default/gpt-5.6-terra',
|
||||
'should honor requested prefixed pro model'
|
||||
);
|
||||
|
||||
const model2 = await capabilityPolicy.resolveChatModel({
|
||||
userId,
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
proModels: ['gemini-2.5-pro', 'claude-sonnet-4-5@20250929'],
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
proModels: ['gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
requestedModelId: 'not-in-optional',
|
||||
paymentEnabled: false,
|
||||
});
|
||||
@@ -2544,14 +2516,10 @@ test('capability policy host should gate pro model requests by subscription stat
|
||||
mockStatus(SubscriptionStatus.Trialing);
|
||||
const model3 = await capabilityPolicy.resolveChatModel({
|
||||
userId,
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
proModels: ['gemini-2.5-pro', 'claude-sonnet-4-5@20250929'],
|
||||
requestedModelId: 'gemini-2.5-pro',
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
proModels: ['gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
requestedModelId: 'gpt-5.6-terra',
|
||||
paymentEnabled: true,
|
||||
});
|
||||
t.snapshot(
|
||||
@@ -2561,45 +2529,33 @@ test('capability policy host should gate pro model requests by subscription stat
|
||||
|
||||
const model3WithPrefix = await capabilityPolicy.resolveChatModel({
|
||||
userId,
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
proModels: ['gemini-2.5-pro', 'claude-sonnet-4-5@20250929'],
|
||||
requestedModelId: 'openai-default/gemini-2.5-pro',
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
proModels: ['gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
requestedModelId: 'openai-default/gpt-5.6-terra',
|
||||
paymentEnabled: true,
|
||||
});
|
||||
t.is(
|
||||
model3WithPrefix,
|
||||
'gemini-2.5-flash',
|
||||
'gpt-5.6-luna',
|
||||
'should fallback to default model when requesting prefixed pro model during trialing'
|
||||
);
|
||||
|
||||
const model4 = await capabilityPolicy.resolveChatModel({
|
||||
userId,
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
proModels: ['gemini-2.5-pro', 'claude-sonnet-4-5@20250929'],
|
||||
requestedModelId: 'gemini-2.5-flash',
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
proModels: ['gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
requestedModelId: 'gpt-5.6-luna',
|
||||
paymentEnabled: true,
|
||||
});
|
||||
t.snapshot(model4, 'should honor requested non-pro model during trialing');
|
||||
|
||||
const model5 = await capabilityPolicy.resolveChatModel({
|
||||
userId,
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
proModels: ['gemini-2.5-pro', 'claude-sonnet-4-5@20250929'],
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
proModels: ['gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
paymentEnabled: true,
|
||||
});
|
||||
t.snapshot(
|
||||
@@ -2613,13 +2569,9 @@ test('capability policy host should gate pro model requests by subscription stat
|
||||
mockStatus(SubscriptionStatus.Active);
|
||||
const model6 = await capabilityPolicy.resolveChatModel({
|
||||
userId,
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
proModels: ['gemini-2.5-pro', 'claude-sonnet-4-5@20250929'],
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
proModels: ['gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
paymentEnabled: true,
|
||||
});
|
||||
t.snapshot(
|
||||
@@ -2629,45 +2581,33 @@ test('capability policy host should gate pro model requests by subscription stat
|
||||
|
||||
const model7 = await capabilityPolicy.resolveChatModel({
|
||||
userId,
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
proModels: ['gemini-2.5-pro', 'claude-sonnet-4-5@20250929'],
|
||||
requestedModelId: 'claude-sonnet-4-5@20250929',
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
proModels: ['gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
requestedModelId: 'claude-sonnet-4-6',
|
||||
paymentEnabled: true,
|
||||
});
|
||||
t.snapshot(model7, 'should honor requested pro model during active');
|
||||
|
||||
const model7WithPrefix = await capabilityPolicy.resolveChatModel({
|
||||
userId,
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
proModels: ['gemini-2.5-pro', 'claude-sonnet-4-5@20250929'],
|
||||
requestedModelId: 'openai-default/claude-sonnet-4-5@20250929',
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
proModels: ['gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
requestedModelId: 'openai-default/claude-sonnet-4-6',
|
||||
paymentEnabled: true,
|
||||
});
|
||||
t.is(
|
||||
model7WithPrefix,
|
||||
'openai-default/claude-sonnet-4-5@20250929',
|
||||
'openai-default/claude-sonnet-4-6',
|
||||
'should honor requested prefixed pro model during active'
|
||||
);
|
||||
|
||||
const model8 = await capabilityPolicy.resolveChatModel({
|
||||
userId,
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: [
|
||||
'gemini-2.5-flash',
|
||||
'gemini-2.5-pro',
|
||||
'claude-sonnet-4-5@20250929',
|
||||
],
|
||||
proModels: ['gemini-2.5-pro', 'claude-sonnet-4-5@20250929'],
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
proModels: ['gpt-5.6-terra', 'claude-sonnet-4-6'],
|
||||
requestedModelId: 'not-in-optional',
|
||||
paymentEnabled: true,
|
||||
});
|
||||
@@ -2684,10 +2624,10 @@ test('prompt runtime should resolve prefixed optional models consistently', asyn
|
||||
const promptName = randomUUID().replaceAll('-', '');
|
||||
await prompt.set(
|
||||
promptName,
|
||||
'gemini-2.5-flash',
|
||||
'gpt-5.6-luna',
|
||||
[{ role: 'user', content: '{{content}}' }],
|
||||
{ proModels: ['gemini-2.5-pro'] },
|
||||
{ optionalModels: ['gemini-2.5-pro'] }
|
||||
{ proModels: ['gpt-5.6-terra'] },
|
||||
{ optionalModels: ['gpt-5.6-terra'] }
|
||||
);
|
||||
|
||||
const textStub = Sinon.stub(chatRuntime, 'text').resolves('ok');
|
||||
@@ -2695,11 +2635,11 @@ test('prompt runtime should resolve prefixed optional models consistently', asyn
|
||||
await promptRuntime.runText(
|
||||
promptName,
|
||||
{ content: 'hello' },
|
||||
{ modelId: 'openai-default/gemini-2.5-pro' }
|
||||
{ modelId: 'openai-default/gpt-5.6-terra' }
|
||||
);
|
||||
t.is(
|
||||
textStub.firstCall.args[0].modelId,
|
||||
'openai-default/gemini-2.5-pro',
|
||||
'openai-default/gpt-5.6-terra',
|
||||
'should preserve accepted provider-prefixed optional model'
|
||||
);
|
||||
|
||||
@@ -2710,7 +2650,7 @@ test('prompt runtime should resolve prefixed optional models consistently', asyn
|
||||
);
|
||||
t.is(
|
||||
textStub.secondCall.args[0].modelId,
|
||||
'gemini-2.5-flash',
|
||||
'gpt-5.6-luna',
|
||||
'should fallback to default model for non-optional prefixed model'
|
||||
);
|
||||
});
|
||||
@@ -2722,10 +2662,10 @@ test('resolver models should use resolved provider metadata for display names',
|
||||
const promptName = randomUUID().replaceAll('-', '');
|
||||
await prompt.set(
|
||||
promptName,
|
||||
'gemini-2.5-flash',
|
||||
'gpt-5.6-luna',
|
||||
[{ role: 'system', content: 'test' }],
|
||||
{ proModels: ['gemini-2.5-pro'] },
|
||||
{ optionalModels: ['gemini-2.5-flash', 'gemini-2.5-pro'] }
|
||||
{ proModels: ['gpt-5.6-terra'] },
|
||||
{ optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra'] }
|
||||
);
|
||||
|
||||
const resolveProvider = Sinon.stub(factory, 'resolveProvider').callsFake(
|
||||
@@ -2754,11 +2694,11 @@ test('resolver models should use resolved provider metadata for display names',
|
||||
const models = await resolver.models(promptName);
|
||||
|
||||
t.deepEqual(models.optionalModels, [
|
||||
{ id: 'gemini-2.5-flash', name: 'Resolved gemini-2.5-flash' },
|
||||
{ id: 'gemini-2.5-pro', name: 'Resolved gemini-2.5-pro' },
|
||||
{ id: 'gpt-5.6-luna', name: 'Resolved gpt-5.6-luna' },
|
||||
{ id: 'gpt-5.6-terra', name: 'Resolved gpt-5.6-terra' },
|
||||
]);
|
||||
t.deepEqual(models.proModels, [
|
||||
{ id: 'gemini-2.5-pro', name: 'Resolved gemini-2.5-pro' },
|
||||
{ id: 'gpt-5.6-terra', name: 'Resolved gpt-5.6-terra' },
|
||||
]);
|
||||
t.true(
|
||||
resolveProvider.alwaysCalledWithMatch({
|
||||
|
||||
@@ -362,7 +362,7 @@ class TestGeminiProvider extends GeminiProvider<{ apiKey: string }> {
|
||||
structuredFactory: (request: LlmStructuredRequest) => LlmStructuredResponse =
|
||||
() => ({
|
||||
id: 'structured_1',
|
||||
model: 'gemini-2.5-flash',
|
||||
model: 'gemini-3.6-flash',
|
||||
output_text: '{"summary":"AFFiNE native"}',
|
||||
output_json: { summary: 'AFFiNE native' },
|
||||
usage: {
|
||||
@@ -1070,7 +1070,7 @@ test('buildCanonicalNativeStructuredRequest should honor explicit structured opt
|
||||
test('buildCanonicalNativeStructuredRequest should honor explicit responseSchema for array outputs', async t => {
|
||||
const schema = z.array(z.object({ speaker: z.string(), text: z.string() }));
|
||||
const { request } = await buildCanonicalNativeStructuredRequest({
|
||||
model: 'gemini-2.5-flash',
|
||||
model: 'gemini-3.6-flash',
|
||||
messages: jsonOnlyPromptMessages('Transcribe this audio.'),
|
||||
options: {},
|
||||
responseContract: buildStructuredResponseContract(schema),
|
||||
@@ -1083,7 +1083,7 @@ test('buildCanonicalNativeStructuredRequest should consume explicit structured r
|
||||
const schema = z.object({ summary: z.string() });
|
||||
const responseContract = buildStructuredResponseContract(schema);
|
||||
const { request } = await buildCanonicalNativeStructuredRequest({
|
||||
model: 'gemini-2.5-flash',
|
||||
model: 'gemini-3.6-flash',
|
||||
messages: jsonOnlyPromptMessages('Summarize AFFiNE.'),
|
||||
options: { strict: false },
|
||||
responseContract,
|
||||
@@ -1120,7 +1120,7 @@ test('buildNativeRequest should canonicalize Gemini attachments', async t => {
|
||||
{
|
||||
title: 'remote file url',
|
||||
input: {
|
||||
model: 'gemini-2.5-flash',
|
||||
model: 'gemini-3.6-flash',
|
||||
messages: [
|
||||
{
|
||||
role: 'user' as const,
|
||||
@@ -1134,7 +1134,7 @@ test('buildNativeRequest should canonicalize Gemini attachments', async t => {
|
||||
{
|
||||
title: 'remote image url',
|
||||
input: {
|
||||
model: 'gemini-2.5-flash',
|
||||
model: 'gemini-3.6-flash',
|
||||
messages: [
|
||||
{
|
||||
role: 'user' as const,
|
||||
@@ -1147,7 +1147,7 @@ test('buildNativeRequest should canonicalize Gemini attachments', async t => {
|
||||
{
|
||||
title: 'data url',
|
||||
input: {
|
||||
model: 'gemini-2.5-flash',
|
||||
model: 'gemini-3.6-flash',
|
||||
messages: [
|
||||
{
|
||||
role: 'user' as const,
|
||||
@@ -1161,7 +1161,7 @@ test('buildNativeRequest should canonicalize Gemini attachments', async t => {
|
||||
{
|
||||
title: 'remote audio url',
|
||||
input: {
|
||||
model: 'gemini-2.5-flash',
|
||||
model: 'gemini-3.6-flash',
|
||||
messages: [
|
||||
{
|
||||
role: 'user' as const,
|
||||
@@ -1175,7 +1175,7 @@ test('buildNativeRequest should canonicalize Gemini attachments', async t => {
|
||||
{
|
||||
title: 'bytes and file handle',
|
||||
input: {
|
||||
model: 'gemini-2.5-flash',
|
||||
model: 'gemini-3.6-flash',
|
||||
messages: [
|
||||
{
|
||||
role: 'user' as const,
|
||||
@@ -1316,7 +1316,7 @@ test('buildNativeRequest should preserve tool schemas and defer Gemini rewrite t
|
||||
const [{ request: geminiRequest }, { request: openaiRequest }] =
|
||||
await Promise.all([
|
||||
buildNativeRequest({
|
||||
model: 'gemini-2.5-flash',
|
||||
model: 'gemini-3.6-flash',
|
||||
messages: promptMessages(userPrompt('read doc')),
|
||||
toolContracts: buildToolContracts({
|
||||
doc_read: defineTool({
|
||||
@@ -1371,7 +1371,7 @@ test('buildNativeStructuredRequest should preserve schemas and defer Gemini rewr
|
||||
const [{ request: geminiRequest }, { request: openaiRequest }] =
|
||||
await Promise.all([
|
||||
buildNativeStructuredRequest({
|
||||
model: 'gemini-2.5-flash',
|
||||
model: 'gemini-3.6-flash',
|
||||
messages: promptMessages(userPrompt('Summarize AFFiNE.')),
|
||||
responseContract: buildStructuredResponseContract(schema),
|
||||
}),
|
||||
@@ -1418,7 +1418,7 @@ test('GeminiProvider should use native path for text-only requests', async t =>
|
||||
const provider = new TestGeminiProvider();
|
||||
|
||||
const result = await getProviderRuntimeHost(provider).run.text(
|
||||
{ modelId: 'gemini-2.5-flash' },
|
||||
{ modelId: 'gemini-3.6-flash' },
|
||||
promptMessages(userPrompt('hello')),
|
||||
{ reasoning: true }
|
||||
);
|
||||
@@ -1439,7 +1439,7 @@ test('GeminiProvider should use native path for structured requests', async t =>
|
||||
|
||||
const schema = z.object({ summary: z.string() });
|
||||
const result = await getProviderRuntimeHost(provider).run.structured(
|
||||
{ modelId: 'gemini-2.5-flash' },
|
||||
{ modelId: 'gemini-3.6-flash' },
|
||||
jsonOnlyPromptMessages('Summarize AFFiNE in one short sentence.'),
|
||||
structuredOptions(schema),
|
||||
structuredContract(schema)
|
||||
@@ -1468,7 +1468,7 @@ test('GeminiProvider should retry when native structured dispatch returns invali
|
||||
}
|
||||
return {
|
||||
id: `structured_retry_${attempts}`,
|
||||
model: 'gemini-2.5-flash',
|
||||
model: 'gemini-3.6-flash',
|
||||
output_text: '{"summary":"ok"}',
|
||||
output_json: { summary: 'ok' },
|
||||
usage: {
|
||||
@@ -1481,7 +1481,7 @@ test('GeminiProvider should retry when native structured dispatch returns invali
|
||||
};
|
||||
|
||||
const result = await getProviderRuntimeHost(provider).run.structured(
|
||||
{ modelId: 'gemini-2.5-flash' },
|
||||
{ modelId: 'gemini-3.6-flash' },
|
||||
jsonOnlyPromptMessages('Summarize AFFiNE in one short sentence.'),
|
||||
structuredOptions(z.object({ summary: z.string() }), { maxRetries: 2 }),
|
||||
structuredContract(z.object({ summary: z.string() }))
|
||||
@@ -1502,7 +1502,7 @@ test('GeminiProvider should treat maxRetries as retry count for backend failures
|
||||
|
||||
const error = await t.throwsAsync(
|
||||
getProviderRuntimeHost(provider).run.structured(
|
||||
{ modelId: 'gemini-2.5-flash' },
|
||||
{ modelId: 'gemini-3.6-flash' },
|
||||
jsonOnlyPromptMessages('Summarize AFFiNE in one short sentence.'),
|
||||
structuredOptions(z.object({ summary: z.string() }), { maxRetries: 2 }),
|
||||
structuredContract(z.object({ summary: z.string() }))
|
||||
@@ -1524,7 +1524,7 @@ test('GeminiProvider should use native structured path for audio attachments', a
|
||||
});
|
||||
provider.structuredFactory = () => ({
|
||||
id: 'structured_audio_1',
|
||||
model: 'gemini-2.5-flash',
|
||||
model: 'gemini-3.6-flash',
|
||||
output_text: '[{"a":"Speaker 1","s":0,"e":1,"t":"Hello"}]',
|
||||
output_json: [{ a: 'Speaker 1', s: 0, e: 1, t: 'Hello' }],
|
||||
usage: { prompt_tokens: 4, completion_tokens: 3, total_tokens: 7 },
|
||||
@@ -1532,7 +1532,7 @@ test('GeminiProvider should use native structured path for audio attachments', a
|
||||
});
|
||||
|
||||
const result = await getProviderRuntimeHost(provider).run.structured(
|
||||
{ modelId: 'gemini-2.5-flash' },
|
||||
{ modelId: 'gemini-3.6-flash' },
|
||||
promptMessages(
|
||||
systemPrompt('Return JSON only.'),
|
||||
userPrompt('transcribe the audio', {
|
||||
@@ -1653,7 +1653,7 @@ test('GeminiProvider should canonicalize native text attachments', async t => {
|
||||
testCase.setup(provider);
|
||||
|
||||
const result = await getProviderRuntimeHost(provider).run.text(
|
||||
{ modelId: 'gemini-2.5-flash' },
|
||||
{ modelId: 'gemini-3.6-flash' },
|
||||
testCase.messages
|
||||
);
|
||||
|
||||
@@ -1677,7 +1677,7 @@ test('GeminiProvider should pass abort signal to remote attachment prefetch', as
|
||||
const controller = new AbortController();
|
||||
|
||||
await getProviderRuntimeHost(provider).run.text(
|
||||
{ modelId: 'gemini-2.5-flash' },
|
||||
{ modelId: 'gemini-3.6-flash' },
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
@@ -1701,7 +1701,7 @@ test('GeminiProvider should not pass materialized inline attachment URL to nativ
|
||||
});
|
||||
|
||||
await getProviderRuntimeHost(provider).run.text(
|
||||
{ modelId: 'gemini-2.5-flash' },
|
||||
{ modelId: 'gemini-3.6-flash' },
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
@@ -1733,7 +1733,7 @@ test('GeminiProvider should reject unsupported attachment schemes at input valid
|
||||
|
||||
const error = await t.throwsAsync(
|
||||
getProviderRuntimeHost(provider).run.text(
|
||||
{ modelId: 'gemini-2.5-flash' },
|
||||
{ modelId: 'gemini-3.6-flash' },
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
@@ -1756,7 +1756,7 @@ test('GeminiProvider should validate malformed attachments before canonicalizati
|
||||
|
||||
const error = await t.throwsAsync(
|
||||
getProviderRuntimeHost(provider).run.text(
|
||||
{ modelId: 'gemini-2.5-flash' },
|
||||
{ modelId: 'gemini-3.6-flash' },
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
@@ -1804,7 +1804,7 @@ test('GeminiProvider should drive tool loop on native path', async t => {
|
||||
};
|
||||
|
||||
const result = await getProviderRuntimeHost(provider).run.text(
|
||||
{ modelId: 'gemini-2.5-flash' },
|
||||
{ modelId: 'gemini-3.6-flash' },
|
||||
[{ role: 'user', content: 'read doc a1' }],
|
||||
{}
|
||||
);
|
||||
@@ -1859,7 +1859,7 @@ test('GeminiVertexProvider should materialize remote attachments before native t
|
||||
});
|
||||
|
||||
const result = await getProviderRuntimeHost(provider).run.text(
|
||||
{ modelId: 'gemini-2.5-flash' },
|
||||
{ modelId: 'gemini-3.6-flash' },
|
||||
[
|
||||
{
|
||||
role: 'user',
|
||||
|
||||
@@ -1443,9 +1443,9 @@ test('checkParams should infer remote image capability from url extension withou
|
||||
test('llmResolveRequestedModelMatch should preserve provider-prefixed optional matches', t => {
|
||||
const request = parseRequestedModelMatchRequest({
|
||||
providerIds: ['openai-default', 'gemini-default'],
|
||||
defaultModel: 'gemini-2.5-flash',
|
||||
optionalModels: ['gemini-2.5-flash', 'gemini-2.5-pro'],
|
||||
requestedModelId: 'openai-default/gemini-2.5-pro',
|
||||
defaultModel: 'gpt-5.6-luna',
|
||||
optionalModels: ['gpt-5.6-luna', 'gpt-5.6-terra'],
|
||||
requestedModelId: 'openai-default/gpt-5.6-terra',
|
||||
});
|
||||
|
||||
t.snapshot(llmResolveRequestedModelMatch(request), 'prefixed optional hit');
|
||||
|
||||
@@ -103,7 +103,7 @@ async function buildNativeTranscriptResult(input: any, runId: string) {
|
||||
openQuestions: [],
|
||||
blockers: [],
|
||||
},
|
||||
providerMeta: { provider: 'gemini', model: 'gemini-2.5-flash' },
|
||||
providerMeta: { provider: 'gemini', model: 'gemini-3.5-flash-lite' },
|
||||
version: 'transcript-result-v1',
|
||||
strategy: 'gemini',
|
||||
},
|
||||
@@ -319,7 +319,7 @@ test('retryTask reuses failed task and queues a new action attempt', async t =>
|
||||
const payload = TranscriptPayloadSchema.parse({
|
||||
normalizedTranscript: '00:00:05 A: Kickoff',
|
||||
summaryJson: null,
|
||||
providerMeta: { provider: 'gemini', model: 'gemini-2.5-flash' },
|
||||
providerMeta: { provider: 'gemini', model: 'gemini-3.5-flash-lite' },
|
||||
});
|
||||
const service = createCopilotTranscriptionService(
|
||||
{
|
||||
@@ -341,7 +341,7 @@ test('retryTask reuses failed task and queues a new action attempt', async t =>
|
||||
} as never,
|
||||
{} as never,
|
||||
{
|
||||
resolveTranscriptionModel: Sinon.stub().resolves('gemini-2.5-flash'),
|
||||
resolveTranscriptionModel: Sinon.stub().resolves('gemini-3.5-flash-lite'),
|
||||
} as never,
|
||||
{} as never,
|
||||
{} as never
|
||||
@@ -382,7 +382,7 @@ test('retryTask prechecks quota or BYOK before queueing provider work', async t
|
||||
{ add } as never,
|
||||
{} as never,
|
||||
{
|
||||
resolveTranscriptionModel: Sinon.stub().resolves('gemini-2.5-flash'),
|
||||
resolveTranscriptionModel: Sinon.stub().resolves('gemini-3.5-flash-lite'),
|
||||
} as never,
|
||||
{} as never,
|
||||
{} as never,
|
||||
@@ -427,7 +427,9 @@ for (const status of ['ready', 'settled']) {
|
||||
} as never,
|
||||
{} as never,
|
||||
{
|
||||
resolveTranscriptionModel: Sinon.stub().resolves('gemini-2.5-flash'),
|
||||
resolveTranscriptionModel: Sinon.stub().resolves(
|
||||
'gemini-3.5-flash-lite'
|
||||
),
|
||||
} as never,
|
||||
{} as never,
|
||||
{} as never
|
||||
@@ -453,7 +455,9 @@ for (const status of ['ready', 'settled']) {
|
||||
|
||||
test('submitTask prechecks quota or BYOK before persisting uploads', async t => {
|
||||
const assertQuotaOrByok = Sinon.stub().rejects(new Error('quota exceeded'));
|
||||
const resolveTranscriptionModel = Sinon.stub().resolves('gemini-2.5-flash');
|
||||
const resolveTranscriptionModel = Sinon.stub().resolves(
|
||||
'gemini-3.5-flash-lite'
|
||||
);
|
||||
const service = createCopilotTranscriptionService(
|
||||
{
|
||||
copilotTranscriptTask: {
|
||||
@@ -492,7 +496,7 @@ test('submitTask rejects unavailable transcript strategy', async t => {
|
||||
{} as never,
|
||||
{} as never,
|
||||
{
|
||||
resolveTranscriptionModel: Sinon.stub().resolves('gemini-2.5-flash'),
|
||||
resolveTranscriptionModel: Sinon.stub().resolves('gemini-3.5-flash-lite'),
|
||||
} as never,
|
||||
{} as never,
|
||||
{} as never
|
||||
@@ -555,7 +559,7 @@ test('transcriptTask runs native transcript recipe through action bridge when av
|
||||
await service.transcriptTask({
|
||||
taskId: 'task-1',
|
||||
payload,
|
||||
modelId: 'gemini-2.5-flash',
|
||||
modelId: 'gemini-3.5-flash-lite',
|
||||
});
|
||||
|
||||
t.like(bridgeInputs[0] as Record<string, unknown>, {
|
||||
@@ -567,7 +571,7 @@ test('transcriptTask runs native transcript recipe through action bridge when av
|
||||
.prepareStructuredRoutes,
|
||||
{
|
||||
stepId: 'transcribe',
|
||||
modelId: 'gemini-2.5-flash',
|
||||
modelId: 'gemini-3.5-flash-lite',
|
||||
}
|
||||
);
|
||||
const messages = (
|
||||
@@ -641,7 +645,7 @@ test('transcriptTask fails task when native action bridge reports an error event
|
||||
service.transcriptTask({
|
||||
taskId: 'task-1',
|
||||
payload,
|
||||
modelId: 'gemini-2.5-flash',
|
||||
modelId: 'gemini-3.5-flash-lite',
|
||||
}),
|
||||
{ message: /native_failed/ }
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user