mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-09-07 01:09:54 +08:00
feat: add upscaler & bg remover (#6967)
This commit is contained in:
@@ -66,6 +66,23 @@ export const prompts: Prompt[] = [
|
|||||||
model: 'fast-turbo-diffusion',
|
model: 'fast-turbo-diffusion',
|
||||||
messages: [],
|
messages: [],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'debug:action:fal-upscaler',
|
||||||
|
action: 'image',
|
||||||
|
model: 'clarity-upscaler',
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: 'user',
|
||||||
|
content: 'best quality, 8K resolution, highres, clarity, {{content}}',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'debug:action:fal-remove-bg',
|
||||||
|
action: 'image',
|
||||||
|
model: 'imageutils/rembg',
|
||||||
|
messages: [],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Summary',
|
name: 'Summary',
|
||||||
action: 'Summary',
|
action: 'Summary',
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ export class CopilotController {
|
|||||||
if (err instanceof HttpException) {
|
if (err instanceof HttpException) {
|
||||||
ret.status = err.getStatus();
|
ret.status = err.getStatus();
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ export type FalConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type FalResponse = {
|
export type FalResponse = {
|
||||||
detail: Array<{ msg: string }>;
|
detail: Array<{ msg: string }> | string;
|
||||||
images: Array<{ url: string }>;
|
images: Array<{ url: string }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -32,6 +32,8 @@ export class FalProvider
|
|||||||
'fast-turbo-diffusion',
|
'fast-turbo-diffusion',
|
||||||
// image to image
|
// image to image
|
||||||
'lcm-sd15-i2i',
|
'lcm-sd15-i2i',
|
||||||
|
'clarity-upscaler',
|
||||||
|
'imageutils/rembg',
|
||||||
];
|
];
|
||||||
|
|
||||||
constructor(private readonly config: FalConfig) {
|
constructor(private readonly config: FalConfig) {
|
||||||
@@ -87,7 +89,11 @@ export class FalProvider
|
|||||||
}).then(res => res.json())) as FalResponse;
|
}).then(res => res.json())) as FalResponse;
|
||||||
|
|
||||||
if (!data.images?.length) {
|
if (!data.images?.length) {
|
||||||
const error = data.detail?.[0]?.msg;
|
const error = Array.isArray(data.detail)
|
||||||
|
? data.detail[0]?.msg
|
||||||
|
: typeof data.detail === 'string'
|
||||||
|
? data.detail
|
||||||
|
: '';
|
||||||
throw new Error(
|
throw new Error(
|
||||||
error ? `Invalid message: ${error}` : 'No images generated'
|
error ? `Invalid message: ${error}` : 'No images generated'
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -77,6 +77,17 @@ export function registerCopilotProvider<
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function unregisterCopilotProvider(type: CopilotProviderType) {
|
||||||
|
COPILOT_PROVIDER.delete(type);
|
||||||
|
ASSERT_CONFIG.delete(type);
|
||||||
|
for (const providers of PROVIDER_CAPABILITY_MAP.values()) {
|
||||||
|
const index = providers.indexOf(type);
|
||||||
|
if (index !== -1) {
|
||||||
|
providers.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Asserts that the config is valid for any registered providers
|
/// Asserts that the config is valid for any registered providers
|
||||||
export function assertProvidersConfigs(config: Config) {
|
export function assertProvidersConfigs(config: Config) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -9,12 +9,16 @@ import Sinon from 'sinon';
|
|||||||
|
|
||||||
import { AuthService } from '../src/core/auth';
|
import { AuthService } from '../src/core/auth';
|
||||||
import { WorkspaceModule } from '../src/core/workspaces';
|
import { WorkspaceModule } from '../src/core/workspaces';
|
||||||
|
import { prompts } from '../src/data/migrations/utils/prompts';
|
||||||
import { ConfigModule } from '../src/fundamentals/config';
|
import { ConfigModule } from '../src/fundamentals/config';
|
||||||
import { CopilotModule } from '../src/plugins/copilot';
|
import { CopilotModule } from '../src/plugins/copilot';
|
||||||
import { PromptService } from '../src/plugins/copilot/prompt';
|
import { PromptService } from '../src/plugins/copilot/prompt';
|
||||||
import {
|
import {
|
||||||
CopilotProviderService,
|
CopilotProviderService,
|
||||||
|
FalProvider,
|
||||||
|
OpenAIProvider,
|
||||||
registerCopilotProvider,
|
registerCopilotProvider,
|
||||||
|
unregisterCopilotProvider,
|
||||||
} from '../src/plugins/copilot/providers';
|
} from '../src/plugins/copilot/providers';
|
||||||
import { CopilotStorage } from '../src/plugins/copilot/storage';
|
import { CopilotStorage } from '../src/plugins/copilot/storage';
|
||||||
import {
|
import {
|
||||||
@@ -80,11 +84,17 @@ test.beforeEach(async t => {
|
|||||||
const user = await signUp(app, 'test', 'darksky@affine.pro', '123456');
|
const user = await signUp(app, 'test', 'darksky@affine.pro', '123456');
|
||||||
token = user.token.token;
|
token = user.token.token;
|
||||||
|
|
||||||
|
unregisterCopilotProvider(OpenAIProvider.type);
|
||||||
|
unregisterCopilotProvider(FalProvider.type);
|
||||||
registerCopilotProvider(MockCopilotTestProvider);
|
registerCopilotProvider(MockCopilotTestProvider);
|
||||||
|
|
||||||
await prompt.set(promptName, 'test', [
|
await prompt.set(promptName, 'test', [
|
||||||
{ role: 'system', content: 'hello {{word}}' },
|
{ role: 'system', content: 'hello {{word}}' },
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
for (const p of prompts) {
|
||||||
|
await prompt.set(p.name, p.model, p.messages);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test.afterEach.always(async t => {
|
test.afterEach.always(async t => {
|
||||||
@@ -218,7 +228,7 @@ test('should be able to chat with api', async t => {
|
|||||||
t.is(
|
t.is(
|
||||||
ret3,
|
ret3,
|
||||||
textToEventStream(
|
textToEventStream(
|
||||||
['https://example.com/image.jpg'],
|
['https://example.com/test.jpg', 'generate text to text stream'],
|
||||||
messageId,
|
messageId,
|
||||||
'attachment'
|
'attachment'
|
||||||
),
|
),
|
||||||
@@ -228,6 +238,51 @@ test('should be able to chat with api', async t => {
|
|||||||
Sinon.restore();
|
Sinon.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('should be able to chat with special image model', async t => {
|
||||||
|
const { app, storage } = t.context;
|
||||||
|
|
||||||
|
Sinon.stub(storage, 'handleRemoteLink').resolvesArg(2);
|
||||||
|
|
||||||
|
const { id } = await createWorkspace(app, token);
|
||||||
|
|
||||||
|
const testWithModel = async (promptName: string, finalPrompt: string) => {
|
||||||
|
const model = prompts.find(p => p.name === promptName)?.model;
|
||||||
|
const sessionId = await createCopilotSession(
|
||||||
|
app,
|
||||||
|
token,
|
||||||
|
id,
|
||||||
|
randomUUID(),
|
||||||
|
promptName
|
||||||
|
);
|
||||||
|
const messageId = await createCopilotMessage(
|
||||||
|
app,
|
||||||
|
token,
|
||||||
|
sessionId,
|
||||||
|
'some-tag',
|
||||||
|
[`https://example.com/${promptName}.jpg`]
|
||||||
|
);
|
||||||
|
const ret3 = await chatWithImages(app, token, sessionId, messageId);
|
||||||
|
t.is(
|
||||||
|
ret3,
|
||||||
|
textToEventStream(
|
||||||
|
[`https://example.com/${model}.jpg`, finalPrompt],
|
||||||
|
messageId,
|
||||||
|
'attachment'
|
||||||
|
),
|
||||||
|
'should be able to chat with images'
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
await testWithModel('debug:action:fal-sd15', 'some-tag');
|
||||||
|
await testWithModel(
|
||||||
|
'debug:action:fal-upscaler',
|
||||||
|
'best quality, 8K resolution, highres, clarity, some-tag'
|
||||||
|
);
|
||||||
|
await testWithModel('debug:action:fal-remove-bg', 'some-tag');
|
||||||
|
|
||||||
|
Sinon.restore();
|
||||||
|
});
|
||||||
|
|
||||||
test('should be able to retry with api', async t => {
|
test('should be able to retry with api', async t => {
|
||||||
const { app, storage } = t.context;
|
const { app, storage } = t.context;
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,13 @@ export class MockCopilotTestProvider
|
|||||||
CopilotImageToImageProvider,
|
CopilotImageToImageProvider,
|
||||||
CopilotImageToTextProvider
|
CopilotImageToTextProvider
|
||||||
{
|
{
|
||||||
override readonly availableModels = ['test'];
|
override readonly availableModels = [
|
||||||
|
'test',
|
||||||
|
'fast-turbo-diffusion',
|
||||||
|
'lcm-sd15-i2i',
|
||||||
|
'clarity-upscaler',
|
||||||
|
'imageutils/rembg',
|
||||||
|
];
|
||||||
static override readonly capabilities = [
|
static override readonly capabilities = [
|
||||||
CopilotCapability.TextToText,
|
CopilotCapability.TextToText,
|
||||||
CopilotCapability.TextToEmbedding,
|
CopilotCapability.TextToEmbedding,
|
||||||
@@ -107,7 +113,7 @@ export class MockCopilotTestProvider
|
|||||||
// ====== text to image ======
|
// ====== text to image ======
|
||||||
override async generateImages(
|
override async generateImages(
|
||||||
messages: PromptMessage[],
|
messages: PromptMessage[],
|
||||||
_model: string = 'test',
|
model: string = 'test',
|
||||||
_options: {
|
_options: {
|
||||||
signal?: AbortSignal;
|
signal?: AbortSignal;
|
||||||
user?: string;
|
user?: string;
|
||||||
@@ -118,7 +124,8 @@ export class MockCopilotTestProvider
|
|||||||
throw new Error('Prompt is required');
|
throw new Error('Prompt is required');
|
||||||
}
|
}
|
||||||
|
|
||||||
return ['https://example.com/image.jpg'];
|
// just let test case can easily verify the final prompt
|
||||||
|
return [`https://example.com/${model}.jpg`, prompt];
|
||||||
}
|
}
|
||||||
|
|
||||||
override async *generateImagesStream(
|
override async *generateImagesStream(
|
||||||
|
|||||||
@@ -6,6 +6,8 @@ export const promptKeys = [
|
|||||||
'debug:action:vision4',
|
'debug:action:vision4',
|
||||||
'debug:action:dalle3',
|
'debug:action:dalle3',
|
||||||
'debug:action:fal-sd15',
|
'debug:action:fal-sd15',
|
||||||
|
'debug:action:fal-upscaler',
|
||||||
|
'debug:action:fal-rembg',
|
||||||
'chat:gpt4',
|
'chat:gpt4',
|
||||||
'Summary',
|
'Summary',
|
||||||
'Summary the webpage',
|
'Summary the webpage',
|
||||||
|
|||||||
Reference in New Issue
Block a user