2025-03-27 12:32:28 +00:00
|
|
|
import { randomBytes } from 'node:crypto';
|
|
|
|
|
|
|
|
import {
|
|
|
|
CopilotChatOptions,
|
|
|
|
CopilotEmbeddingOptions,
|
2025-05-22 06:28:20 +00:00
|
|
|
CopilotImageOptions,
|
|
|
|
CopilotStructuredOptions,
|
|
|
|
ModelConditions,
|
|
|
|
ModelInputType,
|
|
|
|
ModelOutputType,
|
2025-03-27 12:32:28 +00:00
|
|
|
PromptMessage,
|
|
|
|
} from '../../plugins/copilot/providers';
|
|
|
|
import {
|
|
|
|
DEFAULT_DIMENSIONS,
|
|
|
|
OpenAIProvider,
|
|
|
|
} from '../../plugins/copilot/providers/openai';
|
|
|
|
import { sleep } from '../utils/utils';
|
|
|
|
|
|
|
|
export class MockCopilotProvider extends OpenAIProvider {
|
|
|
|
override readonly models = [
|
2025-05-22 06:28:20 +00:00
|
|
|
{
|
|
|
|
id: 'test',
|
|
|
|
capabilities: [
|
|
|
|
{
|
|
|
|
input: [ModelInputType.Text],
|
|
|
|
output: [ModelOutputType.Text],
|
|
|
|
defaultForOutputType: true,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'test-image',
|
|
|
|
capabilities: [
|
|
|
|
{
|
|
|
|
input: [ModelInputType.Text],
|
|
|
|
output: [ModelOutputType.Image],
|
|
|
|
defaultForOutputType: true,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'gpt-4o',
|
|
|
|
capabilities: [
|
|
|
|
{
|
|
|
|
input: [ModelInputType.Text, ModelInputType.Image],
|
|
|
|
output: [ModelOutputType.Text],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'gpt-4o-2024-08-06',
|
|
|
|
capabilities: [
|
|
|
|
{
|
|
|
|
input: [ModelInputType.Text, ModelInputType.Image],
|
|
|
|
output: [ModelOutputType.Text],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'gpt-4.1',
|
|
|
|
capabilities: [
|
|
|
|
{
|
|
|
|
input: [ModelInputType.Text, ModelInputType.Image],
|
|
|
|
output: [ModelOutputType.Text],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'gpt-4.1-2025-04-14',
|
|
|
|
capabilities: [
|
|
|
|
{
|
|
|
|
input: [ModelInputType.Text, ModelInputType.Image],
|
|
|
|
output: [ModelOutputType.Text],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'gpt-4.1-mini',
|
|
|
|
capabilities: [
|
|
|
|
{
|
|
|
|
input: [ModelInputType.Text, ModelInputType.Image],
|
|
|
|
output: [ModelOutputType.Text],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'lcm-sd15-i2i',
|
|
|
|
capabilities: [
|
|
|
|
{
|
|
|
|
input: [ModelInputType.Image],
|
|
|
|
output: [ModelOutputType.Image],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'clarity-upscaler',
|
|
|
|
capabilities: [
|
|
|
|
{
|
|
|
|
input: [ModelInputType.Image],
|
|
|
|
output: [ModelOutputType.Image],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'imageutils/rembg',
|
|
|
|
capabilities: [
|
|
|
|
{
|
|
|
|
input: [ModelInputType.Image],
|
|
|
|
output: [ModelOutputType.Image],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
id: 'gemini-2.5-pro-preview-03-25',
|
|
|
|
capabilities: [
|
|
|
|
{
|
|
|
|
input: [ModelInputType.Text, ModelInputType.Image],
|
|
|
|
output: [ModelOutputType.Text, ModelOutputType.Structured],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
2025-03-27 12:32:28 +00:00
|
|
|
];
|
|
|
|
|
2025-05-22 06:28:20 +00:00
|
|
|
override async text(
|
|
|
|
cond: ModelConditions,
|
2025-03-27 12:32:28 +00:00
|
|
|
messages: PromptMessage[],
|
|
|
|
options: CopilotChatOptions = {}
|
|
|
|
): Promise<string> {
|
2025-05-22 06:28:20 +00:00
|
|
|
const fullCond = {
|
|
|
|
...cond,
|
|
|
|
outputType: ModelOutputType.Text,
|
|
|
|
};
|
|
|
|
await this.checkParams({ messages, cond: fullCond, options });
|
2025-03-27 12:32:28 +00:00
|
|
|
// make some time gap for history test case
|
|
|
|
await sleep(100);
|
|
|
|
return 'generate text to text';
|
|
|
|
}
|
|
|
|
|
2025-05-22 06:28:20 +00:00
|
|
|
override async *streamText(
|
|
|
|
cond: ModelConditions,
|
2025-03-27 12:32:28 +00:00
|
|
|
messages: PromptMessage[],
|
|
|
|
options: CopilotChatOptions = {}
|
|
|
|
): AsyncIterable<string> {
|
2025-05-22 06:28:20 +00:00
|
|
|
const fullCond = { ...cond, outputType: ModelOutputType.Text };
|
|
|
|
await this.checkParams({ messages, cond: fullCond, options });
|
2025-03-27 12:32:28 +00:00
|
|
|
|
|
|
|
// make some time gap for history test case
|
|
|
|
await sleep(100);
|
2025-05-22 06:28:20 +00:00
|
|
|
|
2025-03-27 12:32:28 +00:00
|
|
|
const result = 'generate text to text stream';
|
|
|
|
for (const message of result) {
|
|
|
|
yield message;
|
|
|
|
if (options.signal?.aborted) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-05-22 06:28:20 +00:00
|
|
|
override async structure(
|
|
|
|
cond: ModelConditions,
|
|
|
|
messages: PromptMessage[],
|
|
|
|
options: CopilotStructuredOptions = {}
|
|
|
|
): Promise<string> {
|
|
|
|
const fullCond = { ...cond, outputType: ModelOutputType.Structured };
|
|
|
|
await this.checkParams({ messages, cond: fullCond, options });
|
2025-03-27 12:32:28 +00:00
|
|
|
|
|
|
|
// make some time gap for history test case
|
|
|
|
await sleep(100);
|
2025-05-22 06:28:20 +00:00
|
|
|
return 'generate text to text';
|
2025-03-27 12:32:28 +00:00
|
|
|
}
|
|
|
|
|
2025-05-22 06:28:20 +00:00
|
|
|
override async *streamImages(
|
|
|
|
cond: ModelConditions,
|
2025-03-27 12:32:28 +00:00
|
|
|
messages: PromptMessage[],
|
2025-05-22 06:28:20 +00:00
|
|
|
options: CopilotImageOptions = {}
|
|
|
|
) {
|
|
|
|
const fullCond = { ...cond, outputType: ModelOutputType.Image };
|
|
|
|
await this.checkParams({ messages, cond: fullCond, options });
|
2025-03-27 12:32:28 +00:00
|
|
|
|
|
|
|
// make some time gap for history test case
|
|
|
|
await sleep(100);
|
|
|
|
|
2025-05-22 06:28:20 +00:00
|
|
|
const { content: prompt } = [...messages].pop() || {};
|
|
|
|
if (!prompt) throw new Error('Prompt is required');
|
|
|
|
|
|
|
|
const imageUrls = [
|
|
|
|
`https://example.com/${cond.modelId || 'test'}.jpg`,
|
|
|
|
prompt,
|
|
|
|
];
|
|
|
|
|
|
|
|
for (const imageUrl of imageUrls) {
|
|
|
|
yield imageUrl;
|
|
|
|
if (options.signal?.aborted) {
|
|
|
|
break;
|
|
|
|
}
|
2025-03-27 12:32:28 +00:00
|
|
|
}
|
2025-05-22 06:28:20 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ====== text to embedding ======
|
|
|
|
|
|
|
|
override async embedding(
|
|
|
|
cond: ModelConditions,
|
|
|
|
messages: string | string[],
|
|
|
|
options: CopilotEmbeddingOptions = { dimensions: DEFAULT_DIMENSIONS }
|
|
|
|
): Promise<number[][]> {
|
|
|
|
messages = Array.isArray(messages) ? messages : [messages];
|
|
|
|
const fullCond = { ...cond, outputType: ModelOutputType.Embedding };
|
|
|
|
await this.checkParams({ embeddings: messages, cond: fullCond, options });
|
|
|
|
|
|
|
|
// make some time gap for history test case
|
|
|
|
await sleep(100);
|
|
|
|
return [Array.from(randomBytes(options.dimensions)).map(v => v % 128)];
|
2025-03-27 12:32:28 +00:00
|
|
|
}
|
|
|
|
}
|