Files
upstream-affine/packages/backend/server/src/__tests__/copilot/copilot-provider.spec.ts
upstream-mirror 702cc698a7
Some checks failed
Build & Test / Analyze (javascript, affine) (push) Failing after 4s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 4s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 3s
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 4s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 3s
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Release / Canary Gate (push) Has been skipped
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Copilot Test Automatically / Setup Test (push) Failing after 1s
Release / Prepare (push) Failing after 2s
Release / Release Cloud (push) Has been skipped
Release / Release Desktop (push) Has been skipped
Release / Release Docker Image (push) Has been skipped
Release / Release Mobile (push) Has been skipped
snapshot of https://github.com/toeverything/AFFiNE.git (canary, 2026-09-16; blobs > 131072B omitted)
2026-09-16 04:01:20 +05:00

241 lines
6.6 KiB
TypeScript

import { randomUUID } from 'node:crypto';
import type { TestFn } from 'ava';
import ava from 'ava';
import { Models } from '../../models';
import { CapabilityRuntime } from '../../plugins/copilot/runtime/capability-runtime';
import { CopilotTranscriptionService } from '../../plugins/copilot/transcript';
import { TranscriptPayloadSchema } from '../../plugins/copilot/transcript/schema';
import { createTestingApp, createWorkspace, type TestingApp } from '../utils';
import {
chatWithActionStream,
chatWithImages,
chatWithStreamObject,
chatWithText,
createCopilotMessage,
createCopilotSession,
sse2array,
} from '../utils/copilot';
type Context = {
app: TestingApp;
models: Models;
runtime: CapabilityRuntime;
transcript: CopilotTranscriptionService;
};
const test = ava.serial as TestFn<Context>;
const providerTest =
process.env.AFFINE_TEST_COPILOT_PROVIDER === 'true' ? test : test.skip;
test.before(async t => {
const app = await createTestingApp();
t.context = {
app,
models: app.get(Models),
runtime: app.get(CapabilityRuntime),
transcript: app.get(CopilotTranscriptionService),
};
});
test.beforeEach(async t => {
await t.context.app.initTestingDB();
});
test.after.always(async t => {
await t.context.app?.close();
});
async function assertManagedRoute(
runtime: CapabilityRuntime,
slot: string,
builtInRouteId?: string
) {
await runtime.assertRoute(
slot,
{},
{
builtInRouteId,
quotaBackedRoutesAllowed: true,
}
);
}
providerTest(
'managed text and object routes satisfy the public SSE contract',
async t => {
const { app, runtime } = t.context;
await assertManagedRoute(runtime, 'chat.default', 'Chat With AFFiNE AI');
await app.signupV1();
const workspace = await createWorkspace(app);
const textSession = await createCopilotSession(
app,
workspace.id,
randomUUID(),
'Chat With AFFiNE AI'
);
const textToken = await createCopilotMessage(
app,
textSession,
'Explain AFFiNE in one sentence.'
);
t.truthy((await chatWithText(app, textSession, textToken)).trim());
const objectSession = await createCopilotSession(
app,
workspace.id,
randomUUID(),
'Chat With AFFiNE AI'
);
const objectToken = await createCopilotMessage(
app,
objectSession,
'Explain AFFiNE in one sentence.'
);
const events = sse2array(
await chatWithStreamObject(app, objectSession, objectToken)
);
t.false(events.some(event => event.event === 'error'));
t.true(events.some(event => event.event === 'message'));
}
);
providerTest(
'managed embedding and rerank routes expose final result shapes',
async t => {
const { app, runtime } = t.context;
await assertManagedRoute(runtime, 'index.embedding');
await assertManagedRoute(runtime, 'search.rerank');
const user = await app.signupV1();
const workspace = await createWorkspace(app);
const options = { user: user.id, workspace: workspace.id };
const embeddings = await runtime.embed('route-selected', ['AFFiNE'], {
...options,
featureKind: 'embedding',
});
t.is(embeddings.length, 1);
t.true(embeddings[0].length > 0);
const scores = await runtime.rerank(
'route-selected',
{
query: 'collaborative editor',
candidates: [
{ id: 'relevant', text: 'AFFiNE is a collaborative editor.' },
{ id: 'irrelevant', text: 'A recipe for apple pie.' },
],
},
{ ...options, featureKind: 'rerank' }
);
t.is(scores.length, 2);
t.true(scores.every(score => Number.isFinite(score)));
}
);
providerTest(
'managed image and action routes preserve public event contracts',
async t => {
const { app, runtime } = t.context;
await assertManagedRoute(runtime, 'image.generate', 'Generate image');
await assertManagedRoute(
runtime,
'action.mindmap.generate',
'mindmap.generate'
);
await app.signupV1();
const workspace = await createWorkspace(app);
const imageSession = await createCopilotSession(
app,
workspace.id,
randomUUID(),
'Generate image'
);
const imageToken = await createCopilotMessage(
app,
imageSession,
'A simple panda icon.'
);
const imageEvents = sse2array(
await chatWithImages(app, imageSession, imageToken)
);
t.false(imageEvents.some(event => event.event === 'error'));
t.true(imageEvents.some(event => event.event === 'attachment'));
const actionSession = await createCopilotSession(
app,
workspace.id,
randomUUID(),
'mindmap.generate'
);
const actionToken = await createCopilotMessage(
app,
actionSession,
'AFFiNE product architecture'
);
const actionEvents = sse2array(
await chatWithActionStream(app, actionSession, {
actionId: 'mindmap.generate',
messageId: actionToken,
})
);
t.false(actionEvents.some(event => event.event === 'error'));
t.true(
actionEvents.some(
event =>
event.event === 'message' || event.data?.includes('action_done')
)
);
}
);
providerTest(
'managed transcript route executes the provider-neutral job port',
async t => {
const { models, runtime, transcript } = t.context;
await assertManagedRoute(runtime, 'transcript.audio', 'Transcript audio');
const user = await models.user.create({
email: `copilot-provider-transcript-${randomUUID()}@affine.pro`,
});
const workspace = await models.workspace.create(user.id);
const blobId = `copilot-provider-transcript-${randomUUID()}`;
const payload = TranscriptPayloadSchema.parse({
sourceAudio: { blobId, mimeType: 'audio/mpeg' },
infos: [
{
url: 'https://cdn.affine.pro/copilot-test/MP9qDGuYgnY+ILoEAmHpp3h9Npuw2403EAYMEA.mp3',
mimeType: 'audio/mpeg',
index: 0,
},
],
});
const task = await models.copilotTranscriptTask.create({
userId: user.id,
workspaceId: workspace.id,
blobId,
recipeId: 'transcript.audio',
recipeVersion: 'v1',
dispatchGeneration: 'provider-test-generation',
inputSnapshot: payload,
publicMeta: { sourceAudio: payload.sourceAudio, infos: payload.infos },
});
await transcript.transcriptTask({
taskId: task.id,
payload,
generation: 'provider-test-generation',
scopeMode: 'canonical',
});
const ready = await models.copilotTranscriptTask.get(task.id);
t.is(ready?.status, 'ready');
t.is(
typeof TranscriptPayloadSchema.parse(ready?.protectedResult)
.normalizedTranscript,
'string'
);
}
);