Merge pull request #4791 from janhq/fix/cohere-response-template
fix: cohere response template correction for proper markdown parsing
This commit is contained in:
commit
e98f303d9b
252
core/src/browser/extensions/conversational.test.ts
Normal file
252
core/src/browser/extensions/conversational.test.ts
Normal file
@ -0,0 +1,252 @@
|
||||
import { ConversationalExtension } from './conversational'
|
||||
import { ExtensionTypeEnum } from '../extension'
|
||||
import { Thread, ThreadAssistantInfo, ThreadMessage } from '../../types'
|
||||
|
||||
// Mock implementation of ConversationalExtension
|
||||
class MockConversationalExtension extends ConversationalExtension {
|
||||
private threads: Thread[] = []
|
||||
private messages: { [threadId: string]: ThreadMessage[] } = {}
|
||||
private assistants: { [threadId: string]: ThreadAssistantInfo } = {}
|
||||
|
||||
constructor() {
|
||||
super('http://mock-url.com', 'mock-extension', 'Mock Extension', true, 'A mock extension', '1.0.0')
|
||||
}
|
||||
|
||||
onLoad(): void {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
onUnload(): void {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
async listThreads(): Promise<Thread[]> {
|
||||
return this.threads
|
||||
}
|
||||
|
||||
async createThread(thread: Partial<Thread>): Promise<Thread> {
|
||||
const newThread: Thread = {
|
||||
id: thread.id || `thread-${Date.now()}`,
|
||||
name: thread.name || 'New Thread',
|
||||
createdAt: thread.createdAt || new Date().toISOString(),
|
||||
updatedAt: thread.updatedAt || new Date().toISOString(),
|
||||
}
|
||||
this.threads.push(newThread)
|
||||
this.messages[newThread.id] = []
|
||||
return newThread
|
||||
}
|
||||
|
||||
async modifyThread(thread: Thread): Promise<void> {
|
||||
const index = this.threads.findIndex(t => t.id === thread.id)
|
||||
if (index !== -1) {
|
||||
this.threads[index] = thread
|
||||
}
|
||||
}
|
||||
|
||||
async deleteThread(threadId: string): Promise<void> {
|
||||
this.threads = this.threads.filter(t => t.id !== threadId)
|
||||
delete this.messages[threadId]
|
||||
delete this.assistants[threadId]
|
||||
}
|
||||
|
||||
async createMessage(message: Partial<ThreadMessage>): Promise<ThreadMessage> {
|
||||
if (!message.threadId) throw new Error('Thread ID is required')
|
||||
|
||||
const newMessage: ThreadMessage = {
|
||||
id: message.id || `message-${Date.now()}`,
|
||||
threadId: message.threadId,
|
||||
content: message.content || '',
|
||||
role: message.role || 'user',
|
||||
createdAt: message.createdAt || new Date().toISOString(),
|
||||
}
|
||||
|
||||
if (!this.messages[message.threadId]) {
|
||||
this.messages[message.threadId] = []
|
||||
}
|
||||
|
||||
this.messages[message.threadId].push(newMessage)
|
||||
return newMessage
|
||||
}
|
||||
|
||||
async deleteMessage(threadId: string, messageId: string): Promise<void> {
|
||||
if (this.messages[threadId]) {
|
||||
this.messages[threadId] = this.messages[threadId].filter(m => m.id !== messageId)
|
||||
}
|
||||
}
|
||||
|
||||
async listMessages(threadId: string): Promise<ThreadMessage[]> {
|
||||
return this.messages[threadId] || []
|
||||
}
|
||||
|
||||
async getThreadAssistant(threadId: string): Promise<ThreadAssistantInfo> {
|
||||
return this.assistants[threadId] || { modelId: '', threadId }
|
||||
}
|
||||
|
||||
async createThreadAssistant(
|
||||
threadId: string,
|
||||
assistant: ThreadAssistantInfo
|
||||
): Promise<ThreadAssistantInfo> {
|
||||
this.assistants[threadId] = assistant
|
||||
return assistant
|
||||
}
|
||||
|
||||
async modifyThreadAssistant(
|
||||
threadId: string,
|
||||
assistant: ThreadAssistantInfo
|
||||
): Promise<ThreadAssistantInfo> {
|
||||
this.assistants[threadId] = assistant
|
||||
return assistant
|
||||
}
|
||||
|
||||
async modifyMessage(message: ThreadMessage): Promise<ThreadMessage> {
|
||||
if (!this.messages[message.threadId]) return message
|
||||
|
||||
const index = this.messages[message.threadId].findIndex(m => m.id === message.id)
|
||||
if (index !== -1) {
|
||||
this.messages[message.threadId][index] = message
|
||||
}
|
||||
|
||||
return message
|
||||
}
|
||||
}
|
||||
|
||||
describe('ConversationalExtension', () => {
|
||||
let extension: MockConversationalExtension
|
||||
|
||||
beforeEach(() => {
|
||||
extension = new MockConversationalExtension()
|
||||
})
|
||||
|
||||
test('should return the correct extension type', () => {
|
||||
expect(extension.type()).toBe(ExtensionTypeEnum.Conversational)
|
||||
})
|
||||
|
||||
test('should create and list threads', async () => {
|
||||
const thread = await extension.createThread({ name: 'Test Thread' })
|
||||
expect(thread.name).toBe('Test Thread')
|
||||
|
||||
const threads = await extension.listThreads()
|
||||
expect(threads).toHaveLength(1)
|
||||
expect(threads[0].id).toBe(thread.id)
|
||||
})
|
||||
|
||||
test('should modify thread', async () => {
|
||||
const thread = await extension.createThread({ name: 'Test Thread' })
|
||||
const modifiedThread = { ...thread, name: 'Modified Thread' }
|
||||
|
||||
await extension.modifyThread(modifiedThread)
|
||||
|
||||
const threads = await extension.listThreads()
|
||||
expect(threads[0].name).toBe('Modified Thread')
|
||||
})
|
||||
|
||||
test('should delete thread', async () => {
|
||||
const thread = await extension.createThread({ name: 'Test Thread' })
|
||||
|
||||
await extension.deleteThread(thread.id)
|
||||
|
||||
const threads = await extension.listThreads()
|
||||
expect(threads).toHaveLength(0)
|
||||
})
|
||||
|
||||
test('should create and list messages', async () => {
|
||||
const thread = await extension.createThread({ name: 'Test Thread' })
|
||||
|
||||
const message = await extension.createMessage({
|
||||
threadId: thread.id,
|
||||
content: 'Test message',
|
||||
role: 'user'
|
||||
})
|
||||
|
||||
expect(message.content).toBe('Test message')
|
||||
|
||||
const messages = await extension.listMessages(thread.id)
|
||||
expect(messages).toHaveLength(1)
|
||||
expect(messages[0].id).toBe(message.id)
|
||||
})
|
||||
|
||||
test('should modify message', async () => {
|
||||
const thread = await extension.createThread({ name: 'Test Thread' })
|
||||
|
||||
const message = await extension.createMessage({
|
||||
threadId: thread.id,
|
||||
content: 'Test message',
|
||||
role: 'user'
|
||||
})
|
||||
|
||||
const modifiedMessage = { ...message, content: 'Modified message' }
|
||||
|
||||
await extension.modifyMessage(modifiedMessage)
|
||||
|
||||
const messages = await extension.listMessages(thread.id)
|
||||
expect(messages[0].content).toBe('Modified message')
|
||||
})
|
||||
|
||||
test('should delete message', async () => {
|
||||
const thread = await extension.createThread({ name: 'Test Thread' })
|
||||
|
||||
const message = await extension.createMessage({
|
||||
threadId: thread.id,
|
||||
content: 'Test message',
|
||||
role: 'user'
|
||||
})
|
||||
|
||||
await extension.deleteMessage(thread.id, message.id)
|
||||
|
||||
const messages = await extension.listMessages(thread.id)
|
||||
expect(messages).toHaveLength(0)
|
||||
})
|
||||
|
||||
test('should create and get thread assistant', async () => {
|
||||
const thread = await extension.createThread({ name: 'Test Thread' })
|
||||
|
||||
const assistant: ThreadAssistantInfo = {
|
||||
threadId: thread.id,
|
||||
modelId: 'test-model'
|
||||
}
|
||||
|
||||
await extension.createThreadAssistant(thread.id, assistant)
|
||||
|
||||
const retrievedAssistant = await extension.getThreadAssistant(thread.id)
|
||||
expect(retrievedAssistant.modelId).toBe('test-model')
|
||||
})
|
||||
|
||||
test('should modify thread assistant', async () => {
|
||||
const thread = await extension.createThread({ name: 'Test Thread' })
|
||||
|
||||
const assistant: ThreadAssistantInfo = {
|
||||
threadId: thread.id,
|
||||
modelId: 'test-model'
|
||||
}
|
||||
|
||||
await extension.createThreadAssistant(thread.id, assistant)
|
||||
|
||||
const modifiedAssistant: ThreadAssistantInfo = {
|
||||
threadId: thread.id,
|
||||
modelId: 'modified-model'
|
||||
}
|
||||
|
||||
await extension.modifyThreadAssistant(thread.id, modifiedAssistant)
|
||||
|
||||
const retrievedAssistant = await extension.getThreadAssistant(thread.id)
|
||||
expect(retrievedAssistant.modelId).toBe('modified-model')
|
||||
})
|
||||
|
||||
test('should delete thread assistant when thread is deleted', async () => {
|
||||
const thread = await extension.createThread({ name: 'Test Thread' })
|
||||
|
||||
const assistant: ThreadAssistantInfo = {
|
||||
threadId: thread.id,
|
||||
modelId: 'test-model'
|
||||
}
|
||||
|
||||
await extension.createThreadAssistant(thread.id, assistant)
|
||||
await extension.deleteThread(thread.id)
|
||||
|
||||
// Creating a new thread with the same ID to test if assistant was deleted
|
||||
const newThread = await extension.createThread({ id: thread.id, name: 'New Thread' })
|
||||
const retrievedAssistant = await extension.getThreadAssistant(newThread.id)
|
||||
|
||||
expect(retrievedAssistant.modelId).toBe('')
|
||||
})
|
||||
})
|
||||
566
core/src/browser/extensions/enginesManagement.test.ts
Normal file
566
core/src/browser/extensions/enginesManagement.test.ts
Normal file
@ -0,0 +1,566 @@
|
||||
import { EngineManagementExtension } from './enginesManagement'
|
||||
import { ExtensionTypeEnum } from '../extension'
|
||||
import {
|
||||
EngineConfig,
|
||||
EngineReleased,
|
||||
EngineVariant,
|
||||
Engines,
|
||||
InferenceEngine,
|
||||
DefaultEngineVariant,
|
||||
Model
|
||||
} from '../../types'
|
||||
|
||||
// Mock implementation of EngineManagementExtension
|
||||
class MockEngineManagementExtension extends EngineManagementExtension {
|
||||
private mockEngines: Engines = {
|
||||
llama: {
|
||||
name: 'llama',
|
||||
variants: [
|
||||
{
|
||||
variant: 'cpu',
|
||||
version: '1.0.0',
|
||||
path: '/engines/llama/cpu/1.0.0',
|
||||
installed: true
|
||||
},
|
||||
{
|
||||
variant: 'cuda',
|
||||
version: '1.0.0',
|
||||
path: '/engines/llama/cuda/1.0.0',
|
||||
installed: false
|
||||
}
|
||||
],
|
||||
default: {
|
||||
variant: 'cpu',
|
||||
version: '1.0.0'
|
||||
}
|
||||
},
|
||||
gpt4all: {
|
||||
name: 'gpt4all',
|
||||
variants: [
|
||||
{
|
||||
variant: 'cpu',
|
||||
version: '2.0.0',
|
||||
path: '/engines/gpt4all/cpu/2.0.0',
|
||||
installed: true
|
||||
}
|
||||
],
|
||||
default: {
|
||||
variant: 'cpu',
|
||||
version: '2.0.0'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private mockReleases: { [key: string]: EngineReleased[] } = {
|
||||
'llama-1.0.0': [
|
||||
{
|
||||
variant: 'cpu',
|
||||
version: '1.0.0',
|
||||
os: ['macos', 'linux', 'windows'],
|
||||
url: 'https://example.com/llama/1.0.0/cpu'
|
||||
},
|
||||
{
|
||||
variant: 'cuda',
|
||||
version: '1.0.0',
|
||||
os: ['linux', 'windows'],
|
||||
url: 'https://example.com/llama/1.0.0/cuda'
|
||||
}
|
||||
],
|
||||
'llama-1.1.0': [
|
||||
{
|
||||
variant: 'cpu',
|
||||
version: '1.1.0',
|
||||
os: ['macos', 'linux', 'windows'],
|
||||
url: 'https://example.com/llama/1.1.0/cpu'
|
||||
},
|
||||
{
|
||||
variant: 'cuda',
|
||||
version: '1.1.0',
|
||||
os: ['linux', 'windows'],
|
||||
url: 'https://example.com/llama/1.1.0/cuda'
|
||||
}
|
||||
],
|
||||
'gpt4all-2.0.0': [
|
||||
{
|
||||
variant: 'cpu',
|
||||
version: '2.0.0',
|
||||
os: ['macos', 'linux', 'windows'],
|
||||
url: 'https://example.com/gpt4all/2.0.0/cpu'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
private remoteModels: { [engine: string]: Model[] } = {
|
||||
'llama': [],
|
||||
'gpt4all': []
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super('http://mock-url.com', 'mock-engine-extension', 'Mock Engine Extension', true, 'A mock engine extension', '1.0.0')
|
||||
}
|
||||
|
||||
onLoad(): void {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
onUnload(): void {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
async getEngines(): Promise<Engines> {
|
||||
return JSON.parse(JSON.stringify(this.mockEngines))
|
||||
}
|
||||
|
||||
async getInstalledEngines(name: InferenceEngine): Promise<EngineVariant[]> {
|
||||
if (!this.mockEngines[name]) {
|
||||
return []
|
||||
}
|
||||
|
||||
return this.mockEngines[name].variants.filter(variant => variant.installed)
|
||||
}
|
||||
|
||||
async getReleasedEnginesByVersion(
|
||||
name: InferenceEngine,
|
||||
version: string,
|
||||
platform?: string
|
||||
): Promise<EngineReleased[]> {
|
||||
const key = `${name}-${version}`
|
||||
let releases = this.mockReleases[key] || []
|
||||
|
||||
if (platform) {
|
||||
releases = releases.filter(release => release.os.includes(platform))
|
||||
}
|
||||
|
||||
return releases
|
||||
}
|
||||
|
||||
async getLatestReleasedEngine(
|
||||
name: InferenceEngine,
|
||||
platform?: string
|
||||
): Promise<EngineReleased[]> {
|
||||
// For mock, let's assume latest versions are 1.1.0 for llama and 2.0.0 for gpt4all
|
||||
const latestVersions = {
|
||||
'llama': '1.1.0',
|
||||
'gpt4all': '2.0.0'
|
||||
}
|
||||
|
||||
if (!latestVersions[name]) {
|
||||
return []
|
||||
}
|
||||
|
||||
return this.getReleasedEnginesByVersion(name, latestVersions[name], platform)
|
||||
}
|
||||
|
||||
async installEngine(
|
||||
name: string,
|
||||
engineConfig: EngineConfig
|
||||
): Promise<{ messages: string }> {
|
||||
if (!this.mockEngines[name]) {
|
||||
this.mockEngines[name] = {
|
||||
name,
|
||||
variants: [],
|
||||
default: {
|
||||
variant: engineConfig.variant,
|
||||
version: engineConfig.version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if variant already exists
|
||||
const existingVariantIndex = this.mockEngines[name].variants.findIndex(
|
||||
v => v.variant === engineConfig.variant && v.version === engineConfig.version
|
||||
)
|
||||
|
||||
if (existingVariantIndex >= 0) {
|
||||
this.mockEngines[name].variants[existingVariantIndex].installed = true
|
||||
} else {
|
||||
this.mockEngines[name].variants.push({
|
||||
variant: engineConfig.variant,
|
||||
version: engineConfig.version,
|
||||
path: `/engines/${name}/${engineConfig.variant}/${engineConfig.version}`,
|
||||
installed: true
|
||||
})
|
||||
}
|
||||
|
||||
return { messages: `Successfully installed ${name} ${engineConfig.variant} ${engineConfig.version}` }
|
||||
}
|
||||
|
||||
async addRemoteEngine(
|
||||
engineConfig: EngineConfig
|
||||
): Promise<{ messages: string }> {
|
||||
const name = engineConfig.name || 'remote-engine'
|
||||
|
||||
if (!this.mockEngines[name]) {
|
||||
this.mockEngines[name] = {
|
||||
name,
|
||||
variants: [],
|
||||
default: {
|
||||
variant: engineConfig.variant,
|
||||
version: engineConfig.version
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.mockEngines[name].variants.push({
|
||||
variant: engineConfig.variant,
|
||||
version: engineConfig.version,
|
||||
path: engineConfig.path || `/engines/${name}/${engineConfig.variant}/${engineConfig.version}`,
|
||||
installed: true,
|
||||
url: engineConfig.url
|
||||
})
|
||||
|
||||
return { messages: `Successfully added remote engine ${name}` }
|
||||
}
|
||||
|
||||
async uninstallEngine(
|
||||
name: InferenceEngine,
|
||||
engineConfig: EngineConfig
|
||||
): Promise<{ messages: string }> {
|
||||
if (!this.mockEngines[name]) {
|
||||
return { messages: `Engine ${name} not found` }
|
||||
}
|
||||
|
||||
const variantIndex = this.mockEngines[name].variants.findIndex(
|
||||
v => v.variant === engineConfig.variant && v.version === engineConfig.version
|
||||
)
|
||||
|
||||
if (variantIndex >= 0) {
|
||||
this.mockEngines[name].variants[variantIndex].installed = false
|
||||
|
||||
// If this was the default variant, reset default
|
||||
if (
|
||||
this.mockEngines[name].default.variant === engineConfig.variant &&
|
||||
this.mockEngines[name].default.version === engineConfig.version
|
||||
) {
|
||||
// Find another installed variant to set as default
|
||||
const installedVariant = this.mockEngines[name].variants.find(v => v.installed)
|
||||
if (installedVariant) {
|
||||
this.mockEngines[name].default = {
|
||||
variant: installedVariant.variant,
|
||||
version: installedVariant.version
|
||||
}
|
||||
} else {
|
||||
// No installed variants remain, clear default
|
||||
this.mockEngines[name].default = { variant: '', version: '' }
|
||||
}
|
||||
}
|
||||
|
||||
return { messages: `Successfully uninstalled ${name} ${engineConfig.variant} ${engineConfig.version}` }
|
||||
} else {
|
||||
return { messages: `Variant ${engineConfig.variant} ${engineConfig.version} not found for engine ${name}` }
|
||||
}
|
||||
}
|
||||
|
||||
async getDefaultEngineVariant(
|
||||
name: InferenceEngine
|
||||
): Promise<DefaultEngineVariant> {
|
||||
if (!this.mockEngines[name]) {
|
||||
return { variant: '', version: '' }
|
||||
}
|
||||
|
||||
return this.mockEngines[name].default
|
||||
}
|
||||
|
||||
async setDefaultEngineVariant(
|
||||
name: InferenceEngine,
|
||||
engineConfig: EngineConfig
|
||||
): Promise<{ messages: string }> {
|
||||
if (!this.mockEngines[name]) {
|
||||
return { messages: `Engine ${name} not found` }
|
||||
}
|
||||
|
||||
const variantExists = this.mockEngines[name].variants.some(
|
||||
v => v.variant === engineConfig.variant && v.version === engineConfig.version && v.installed
|
||||
)
|
||||
|
||||
if (!variantExists) {
|
||||
return { messages: `Variant ${engineConfig.variant} ${engineConfig.version} not found or not installed` }
|
||||
}
|
||||
|
||||
this.mockEngines[name].default = {
|
||||
variant: engineConfig.variant,
|
||||
version: engineConfig.version
|
||||
}
|
||||
|
||||
return { messages: `Successfully set ${engineConfig.variant} ${engineConfig.version} as default for ${name}` }
|
||||
}
|
||||
|
||||
async updateEngine(
|
||||
name: InferenceEngine,
|
||||
engineConfig?: EngineConfig
|
||||
): Promise<{ messages: string }> {
|
||||
if (!this.mockEngines[name]) {
|
||||
return { messages: `Engine ${name} not found` }
|
||||
}
|
||||
|
||||
if (!engineConfig) {
|
||||
// Assume we're updating to the latest version
|
||||
return { messages: `Successfully updated ${name} to the latest version` }
|
||||
}
|
||||
|
||||
const variantIndex = this.mockEngines[name].variants.findIndex(
|
||||
v => v.variant === engineConfig.variant && v.installed
|
||||
)
|
||||
|
||||
if (variantIndex >= 0) {
|
||||
// Update the version
|
||||
this.mockEngines[name].variants[variantIndex].version = engineConfig.version
|
||||
|
||||
// If this was the default variant, update default version too
|
||||
if (this.mockEngines[name].default.variant === engineConfig.variant) {
|
||||
this.mockEngines[name].default.version = engineConfig.version
|
||||
}
|
||||
|
||||
return { messages: `Successfully updated ${name} ${engineConfig.variant} to version ${engineConfig.version}` }
|
||||
} else {
|
||||
return { messages: `Installed variant ${engineConfig.variant} not found for engine ${name}` }
|
||||
}
|
||||
}
|
||||
|
||||
async addRemoteModel(model: Model): Promise<void> {
|
||||
const engine = model.engine as string
|
||||
|
||||
if (!this.remoteModels[engine]) {
|
||||
this.remoteModels[engine] = []
|
||||
}
|
||||
|
||||
this.remoteModels[engine].push(model)
|
||||
}
|
||||
|
||||
async getRemoteModels(name: InferenceEngine | string): Promise<Model[]> {
|
||||
return this.remoteModels[name] || []
|
||||
}
|
||||
}
|
||||
|
||||
describe('EngineManagementExtension', () => {
|
||||
let extension: MockEngineManagementExtension
|
||||
|
||||
beforeEach(() => {
|
||||
extension = new MockEngineManagementExtension()
|
||||
})
|
||||
|
||||
test('should return the correct extension type', () => {
|
||||
expect(extension.type()).toBe(ExtensionTypeEnum.Engine)
|
||||
})
|
||||
|
||||
test('should get all engines', async () => {
|
||||
const engines = await extension.getEngines()
|
||||
|
||||
expect(engines).toBeDefined()
|
||||
expect(engines.llama).toBeDefined()
|
||||
expect(engines.gpt4all).toBeDefined()
|
||||
expect(engines.llama.variants).toHaveLength(2)
|
||||
expect(engines.gpt4all.variants).toHaveLength(1)
|
||||
})
|
||||
|
||||
test('should get installed engines', async () => {
|
||||
const llamaEngines = await extension.getInstalledEngines('llama')
|
||||
|
||||
expect(llamaEngines).toHaveLength(1)
|
||||
expect(llamaEngines[0].variant).toBe('cpu')
|
||||
expect(llamaEngines[0].installed).toBe(true)
|
||||
|
||||
const gpt4allEngines = await extension.getInstalledEngines('gpt4all')
|
||||
|
||||
expect(gpt4allEngines).toHaveLength(1)
|
||||
expect(gpt4allEngines[0].variant).toBe('cpu')
|
||||
expect(gpt4allEngines[0].installed).toBe(true)
|
||||
|
||||
// Test non-existent engine
|
||||
const nonExistentEngines = await extension.getInstalledEngines('non-existent' as InferenceEngine)
|
||||
expect(nonExistentEngines).toHaveLength(0)
|
||||
})
|
||||
|
||||
test('should get released engines by version', async () => {
|
||||
const llamaReleases = await extension.getReleasedEnginesByVersion('llama', '1.0.0')
|
||||
|
||||
expect(llamaReleases).toHaveLength(2)
|
||||
expect(llamaReleases[0].variant).toBe('cpu')
|
||||
expect(llamaReleases[1].variant).toBe('cuda')
|
||||
|
||||
// Test with platform filter
|
||||
const llamaLinuxReleases = await extension.getReleasedEnginesByVersion('llama', '1.0.0', 'linux')
|
||||
|
||||
expect(llamaLinuxReleases).toHaveLength(2)
|
||||
|
||||
const llamaMacReleases = await extension.getReleasedEnginesByVersion('llama', '1.0.0', 'macos')
|
||||
|
||||
expect(llamaMacReleases).toHaveLength(1)
|
||||
expect(llamaMacReleases[0].variant).toBe('cpu')
|
||||
|
||||
// Test non-existent version
|
||||
const nonExistentReleases = await extension.getReleasedEnginesByVersion('llama', '9.9.9')
|
||||
expect(nonExistentReleases).toHaveLength(0)
|
||||
})
|
||||
|
||||
test('should get latest released engines', async () => {
|
||||
const latestLlamaReleases = await extension.getLatestReleasedEngine('llama')
|
||||
|
||||
expect(latestLlamaReleases).toHaveLength(2)
|
||||
expect(latestLlamaReleases[0].version).toBe('1.1.0')
|
||||
|
||||
// Test with platform filter
|
||||
const latestLlamaMacReleases = await extension.getLatestReleasedEngine('llama', 'macos')
|
||||
|
||||
expect(latestLlamaMacReleases).toHaveLength(1)
|
||||
expect(latestLlamaMacReleases[0].variant).toBe('cpu')
|
||||
expect(latestLlamaMacReleases[0].version).toBe('1.1.0')
|
||||
|
||||
// Test non-existent engine
|
||||
const nonExistentReleases = await extension.getLatestReleasedEngine('non-existent' as InferenceEngine)
|
||||
expect(nonExistentReleases).toHaveLength(0)
|
||||
})
|
||||
|
||||
test('should install engine', async () => {
|
||||
// Install existing engine variant that is not installed
|
||||
const result = await extension.installEngine('llama', { variant: 'cuda', version: '1.0.0' })
|
||||
|
||||
expect(result.messages).toContain('Successfully installed')
|
||||
|
||||
const installedEngines = await extension.getInstalledEngines('llama')
|
||||
expect(installedEngines).toHaveLength(2)
|
||||
expect(installedEngines.some(e => e.variant === 'cuda')).toBe(true)
|
||||
|
||||
// Install non-existent engine
|
||||
const newEngineResult = await extension.installEngine('new-engine', { variant: 'cpu', version: '1.0.0' })
|
||||
|
||||
expect(newEngineResult.messages).toContain('Successfully installed')
|
||||
|
||||
const engines = await extension.getEngines()
|
||||
expect(engines['new-engine']).toBeDefined()
|
||||
expect(engines['new-engine'].variants).toHaveLength(1)
|
||||
expect(engines['new-engine'].variants[0].installed).toBe(true)
|
||||
})
|
||||
|
||||
test('should add remote engine', async () => {
|
||||
const result = await extension.addRemoteEngine({
|
||||
name: 'remote-llm',
|
||||
variant: 'remote',
|
||||
version: '1.0.0',
|
||||
url: 'https://example.com/remote-llm-api'
|
||||
})
|
||||
|
||||
expect(result.messages).toContain('Successfully added remote engine')
|
||||
|
||||
const engines = await extension.getEngines()
|
||||
expect(engines['remote-llm']).toBeDefined()
|
||||
expect(engines['remote-llm'].variants).toHaveLength(1)
|
||||
expect(engines['remote-llm'].variants[0].url).toBe('https://example.com/remote-llm-api')
|
||||
})
|
||||
|
||||
test('should uninstall engine', async () => {
|
||||
const result = await extension.uninstallEngine('llama', { variant: 'cpu', version: '1.0.0' })
|
||||
|
||||
expect(result.messages).toContain('Successfully uninstalled')
|
||||
|
||||
const installedEngines = await extension.getInstalledEngines('llama')
|
||||
expect(installedEngines).toHaveLength(0)
|
||||
|
||||
// Test uninstalling non-existent variant
|
||||
const nonExistentResult = await extension.uninstallEngine('llama', { variant: 'non-existent', version: '1.0.0' })
|
||||
|
||||
expect(nonExistentResult.messages).toContain('not found')
|
||||
})
|
||||
|
||||
test('should handle default variant when uninstalling', async () => {
|
||||
// First install cuda variant
|
||||
await extension.installEngine('llama', { variant: 'cuda', version: '1.0.0' })
|
||||
|
||||
// Set cuda as default
|
||||
await extension.setDefaultEngineVariant('llama', { variant: 'cuda', version: '1.0.0' })
|
||||
|
||||
// Check that cuda is now default
|
||||
let defaultVariant = await extension.getDefaultEngineVariant('llama')
|
||||
expect(defaultVariant.variant).toBe('cuda')
|
||||
|
||||
// Uninstall cuda
|
||||
await extension.uninstallEngine('llama', { variant: 'cuda', version: '1.0.0' })
|
||||
|
||||
// Check that default has changed to another installed variant
|
||||
defaultVariant = await extension.getDefaultEngineVariant('llama')
|
||||
expect(defaultVariant.variant).toBe('cpu')
|
||||
|
||||
// Uninstall all variants
|
||||
await extension.uninstallEngine('llama', { variant: 'cpu', version: '1.0.0' })
|
||||
|
||||
// Check that default is now empty
|
||||
defaultVariant = await extension.getDefaultEngineVariant('llama')
|
||||
expect(defaultVariant.variant).toBe('')
|
||||
expect(defaultVariant.version).toBe('')
|
||||
})
|
||||
|
||||
test('should get default engine variant', async () => {
|
||||
const llamaDefault = await extension.getDefaultEngineVariant('llama')
|
||||
|
||||
expect(llamaDefault.variant).toBe('cpu')
|
||||
expect(llamaDefault.version).toBe('1.0.0')
|
||||
|
||||
// Test non-existent engine
|
||||
const nonExistentDefault = await extension.getDefaultEngineVariant('non-existent' as InferenceEngine)
|
||||
expect(nonExistentDefault.variant).toBe('')
|
||||
expect(nonExistentDefault.version).toBe('')
|
||||
})
|
||||
|
||||
test('should set default engine variant', async () => {
|
||||
// Install cuda variant
|
||||
await extension.installEngine('llama', { variant: 'cuda', version: '1.0.0' })
|
||||
|
||||
const result = await extension.setDefaultEngineVariant('llama', { variant: 'cuda', version: '1.0.0' })
|
||||
|
||||
expect(result.messages).toContain('Successfully set')
|
||||
|
||||
const defaultVariant = await extension.getDefaultEngineVariant('llama')
|
||||
expect(defaultVariant.variant).toBe('cuda')
|
||||
expect(defaultVariant.version).toBe('1.0.0')
|
||||
|
||||
// Test setting non-existent variant as default
|
||||
const nonExistentResult = await extension.setDefaultEngineVariant('llama', { variant: 'non-existent', version: '1.0.0' })
|
||||
|
||||
expect(nonExistentResult.messages).toContain('not found')
|
||||
})
|
||||
|
||||
test('should update engine', async () => {
|
||||
const result = await extension.updateEngine('llama', { variant: 'cpu', version: '1.1.0' })
|
||||
|
||||
expect(result.messages).toContain('Successfully updated')
|
||||
|
||||
const engines = await extension.getEngines()
|
||||
const cpuVariant = engines.llama.variants.find(v => v.variant === 'cpu')
|
||||
expect(cpuVariant).toBeDefined()
|
||||
expect(cpuVariant?.version).toBe('1.1.0')
|
||||
|
||||
// Default should also be updated since cpu was default
|
||||
expect(engines.llama.default.version).toBe('1.1.0')
|
||||
|
||||
// Test updating non-existent variant
|
||||
const nonExistentResult = await extension.updateEngine('llama', { variant: 'non-existent', version: '1.1.0' })
|
||||
|
||||
expect(nonExistentResult.messages).toContain('not found')
|
||||
})
|
||||
|
||||
test('should add and get remote models', async () => {
|
||||
const model: Model = {
|
||||
id: 'remote-model-1',
|
||||
name: 'Remote Model 1',
|
||||
path: '/path/to/remote-model',
|
||||
engine: 'llama',
|
||||
format: 'gguf',
|
||||
modelFormat: 'gguf',
|
||||
source: 'remote',
|
||||
status: 'ready',
|
||||
contextLength: 4096,
|
||||
sizeInGB: 4,
|
||||
created: new Date().toISOString()
|
||||
}
|
||||
|
||||
await extension.addRemoteModel(model)
|
||||
|
||||
const llamaModels = await extension.getRemoteModels('llama')
|
||||
expect(llamaModels).toHaveLength(1)
|
||||
expect(llamaModels[0].id).toBe('remote-model-1')
|
||||
|
||||
// Test non-existent engine
|
||||
const nonExistentModels = await extension.getRemoteModels('non-existent')
|
||||
expect(nonExistentModels).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
146
core/src/browser/extensions/hardwareManagement.test.ts
Normal file
146
core/src/browser/extensions/hardwareManagement.test.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import { HardwareManagementExtension } from './hardwareManagement'
|
||||
import { ExtensionTypeEnum } from '../extension'
|
||||
import { HardwareInformation } from '../../types'
|
||||
|
||||
// Mock implementation of HardwareManagementExtension
|
||||
class MockHardwareManagementExtension extends HardwareManagementExtension {
|
||||
private activeGpus: number[] = [0]
|
||||
private mockHardwareInfo: HardwareInformation = {
|
||||
cpu: {
|
||||
manufacturer: 'Mock CPU Manufacturer',
|
||||
brand: 'Mock CPU',
|
||||
cores: 8,
|
||||
physicalCores: 4,
|
||||
speed: 3.5,
|
||||
},
|
||||
memory: {
|
||||
total: 16 * 1024 * 1024 * 1024, // 16GB in bytes
|
||||
free: 8 * 1024 * 1024 * 1024, // 8GB in bytes
|
||||
},
|
||||
gpus: [
|
||||
{
|
||||
id: 0,
|
||||
vendor: 'Mock GPU Vendor',
|
||||
model: 'Mock GPU Model 1',
|
||||
memory: 8 * 1024 * 1024 * 1024, // 8GB in bytes
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
vendor: 'Mock GPU Vendor',
|
||||
model: 'Mock GPU Model 2',
|
||||
memory: 4 * 1024 * 1024 * 1024, // 4GB in bytes
|
||||
}
|
||||
],
|
||||
active_gpus: [0],
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super('http://mock-url.com', 'mock-hardware-extension', 'Mock Hardware Extension', true, 'A mock hardware extension', '1.0.0')
|
||||
}
|
||||
|
||||
onLoad(): void {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
onUnload(): void {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
async getHardware(): Promise<HardwareInformation> {
|
||||
// Return a copy to prevent test side effects
|
||||
return JSON.parse(JSON.stringify(this.mockHardwareInfo))
|
||||
}
|
||||
|
||||
async setAvtiveGpu(data: { gpus: number[] }): Promise<{
|
||||
message: string
|
||||
activated_gpus: number[]
|
||||
}> {
|
||||
// Validate GPUs exist
|
||||
const validGpus = data.gpus.filter(gpuId =>
|
||||
this.mockHardwareInfo.gpus.some(gpu => gpu.id === gpuId)
|
||||
)
|
||||
|
||||
if (validGpus.length === 0) {
|
||||
throw new Error('No valid GPUs selected')
|
||||
}
|
||||
|
||||
// Update active GPUs
|
||||
this.activeGpus = validGpus
|
||||
this.mockHardwareInfo.active_gpus = validGpus
|
||||
|
||||
return {
|
||||
message: 'GPU activation successful',
|
||||
activated_gpus: validGpus
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
describe('HardwareManagementExtension', () => {
|
||||
let extension: MockHardwareManagementExtension
|
||||
|
||||
beforeEach(() => {
|
||||
extension = new MockHardwareManagementExtension()
|
||||
})
|
||||
|
||||
test('should return the correct extension type', () => {
|
||||
expect(extension.type()).toBe(ExtensionTypeEnum.Hardware)
|
||||
})
|
||||
|
||||
test('should get hardware information', async () => {
|
||||
const hardwareInfo = await extension.getHardware()
|
||||
|
||||
// Check CPU info
|
||||
expect(hardwareInfo.cpu).toBeDefined()
|
||||
expect(hardwareInfo.cpu.manufacturer).toBe('Mock CPU Manufacturer')
|
||||
expect(hardwareInfo.cpu.cores).toBe(8)
|
||||
|
||||
// Check memory info
|
||||
expect(hardwareInfo.memory).toBeDefined()
|
||||
expect(hardwareInfo.memory.total).toBe(16 * 1024 * 1024 * 1024)
|
||||
|
||||
// Check GPU info
|
||||
expect(hardwareInfo.gpus).toHaveLength(2)
|
||||
expect(hardwareInfo.gpus[0].model).toBe('Mock GPU Model 1')
|
||||
expect(hardwareInfo.gpus[1].model).toBe('Mock GPU Model 2')
|
||||
|
||||
// Check active GPUs
|
||||
expect(hardwareInfo.active_gpus).toEqual([0])
|
||||
})
|
||||
|
||||
test('should set active GPUs', async () => {
|
||||
const result = await extension.setAvtiveGpu({ gpus: [1] })
|
||||
|
||||
expect(result.message).toBe('GPU activation successful')
|
||||
expect(result.activated_gpus).toEqual([1])
|
||||
|
||||
// Verify the change in hardware info
|
||||
const hardwareInfo = await extension.getHardware()
|
||||
expect(hardwareInfo.active_gpus).toEqual([1])
|
||||
})
|
||||
|
||||
test('should set multiple active GPUs', async () => {
|
||||
const result = await extension.setAvtiveGpu({ gpus: [0, 1] })
|
||||
|
||||
expect(result.message).toBe('GPU activation successful')
|
||||
expect(result.activated_gpus).toEqual([0, 1])
|
||||
|
||||
// Verify the change in hardware info
|
||||
const hardwareInfo = await extension.getHardware()
|
||||
expect(hardwareInfo.active_gpus).toEqual([0, 1])
|
||||
})
|
||||
|
||||
test('should throw error for invalid GPU ids', async () => {
|
||||
await expect(extension.setAvtiveGpu({ gpus: [999] })).rejects.toThrow('No valid GPUs selected')
|
||||
})
|
||||
|
||||
test('should handle mix of valid and invalid GPU ids', async () => {
|
||||
const result = await extension.setAvtiveGpu({ gpus: [0, 999] })
|
||||
|
||||
// Should only activate valid GPUs
|
||||
expect(result.activated_gpus).toEqual([0])
|
||||
|
||||
// Verify the change in hardware info
|
||||
const hardwareInfo = await extension.getHardware()
|
||||
expect(hardwareInfo.active_gpus).toEqual([0])
|
||||
})
|
||||
})
|
||||
286
core/src/browser/extensions/model.test.ts
Normal file
286
core/src/browser/extensions/model.test.ts
Normal file
@ -0,0 +1,286 @@
|
||||
import { ModelExtension } from './model'
|
||||
import { ExtensionTypeEnum } from '../extension'
|
||||
import { Model, OptionType, ModelSource } from '../../types'
|
||||
|
||||
// Mock implementation of ModelExtension
|
||||
class MockModelExtension extends ModelExtension {
|
||||
private models: Model[] = []
|
||||
private sources: ModelSource[] = []
|
||||
private loadedModels: Set<string> = new Set()
|
||||
private modelsPulling: Set<string> = new Set()
|
||||
|
||||
constructor() {
|
||||
super('http://mock-url.com', 'mock-model-extension', 'Mock Model Extension', true, 'A mock model extension', '1.0.0')
|
||||
}
|
||||
|
||||
onLoad(): void {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
onUnload(): void {
|
||||
// Mock implementation
|
||||
}
|
||||
|
||||
async configurePullOptions(configs: { [key: string]: any }): Promise<any> {
|
||||
return configs
|
||||
}
|
||||
|
||||
async getModels(): Promise<Model[]> {
|
||||
return this.models
|
||||
}
|
||||
|
||||
async pullModel(model: string, id?: string, name?: string): Promise<void> {
|
||||
const modelId = id || `model-${Date.now()}`
|
||||
this.modelsPulling.add(modelId)
|
||||
|
||||
// Simulate model pull by adding it to the model list
|
||||
const newModel: Model = {
|
||||
id: modelId,
|
||||
path: `/models/${model}`,
|
||||
name: name || model,
|
||||
source: 'mock-source',
|
||||
modelFormat: 'mock-format',
|
||||
engine: 'mock-engine',
|
||||
format: 'mock-format',
|
||||
status: 'ready',
|
||||
contextLength: 2048,
|
||||
sizeInGB: 2,
|
||||
created: new Date().toISOString(),
|
||||
pullProgress: {
|
||||
percent: 100,
|
||||
transferred: 0,
|
||||
total: 0
|
||||
}
|
||||
}
|
||||
|
||||
this.models.push(newModel)
|
||||
this.loadedModels.add(modelId)
|
||||
this.modelsPulling.delete(modelId)
|
||||
}
|
||||
|
||||
async cancelModelPull(modelId: string): Promise<void> {
|
||||
this.modelsPulling.delete(modelId)
|
||||
// Remove the model if it's in the pulling state
|
||||
this.models = this.models.filter(m => m.id !== modelId)
|
||||
}
|
||||
|
||||
async importModel(
|
||||
model: string,
|
||||
modelPath: string,
|
||||
name?: string,
|
||||
optionType?: OptionType
|
||||
): Promise<void> {
|
||||
const newModel: Model = {
|
||||
id: `model-${Date.now()}`,
|
||||
path: modelPath,
|
||||
name: name || model,
|
||||
source: 'local',
|
||||
modelFormat: optionType?.format || 'mock-format',
|
||||
engine: optionType?.engine || 'mock-engine',
|
||||
format: optionType?.format || 'mock-format',
|
||||
status: 'ready',
|
||||
contextLength: optionType?.contextLength || 2048,
|
||||
sizeInGB: 2,
|
||||
created: new Date().toISOString(),
|
||||
}
|
||||
|
||||
this.models.push(newModel)
|
||||
this.loadedModels.add(newModel.id)
|
||||
}
|
||||
|
||||
async updateModel(modelInfo: Partial<Model>): Promise<Model> {
|
||||
if (!modelInfo.id) throw new Error('Model ID is required')
|
||||
|
||||
const index = this.models.findIndex(m => m.id === modelInfo.id)
|
||||
if (index === -1) throw new Error('Model not found')
|
||||
|
||||
this.models[index] = { ...this.models[index], ...modelInfo }
|
||||
return this.models[index]
|
||||
}
|
||||
|
||||
async deleteModel(modelId: string): Promise<void> {
|
||||
this.models = this.models.filter(m => m.id !== modelId)
|
||||
this.loadedModels.delete(modelId)
|
||||
}
|
||||
|
||||
async isModelLoaded(modelId: string): Promise<boolean> {
|
||||
return this.loadedModels.has(modelId)
|
||||
}
|
||||
|
||||
async getSources(): Promise<ModelSource[]> {
|
||||
return this.sources
|
||||
}
|
||||
|
||||
async addSource(source: string): Promise<void> {
|
||||
const newSource: ModelSource = {
|
||||
id: `source-${Date.now()}`,
|
||||
url: source,
|
||||
name: `Source ${this.sources.length + 1}`,
|
||||
type: 'mock-type'
|
||||
}
|
||||
|
||||
this.sources.push(newSource)
|
||||
}
|
||||
|
||||
async deleteSource(sourceId: string): Promise<void> {
|
||||
this.sources = this.sources.filter(s => s.id !== sourceId)
|
||||
}
|
||||
}
|
||||
|
||||
describe('ModelExtension', () => {
|
||||
let extension: MockModelExtension
|
||||
|
||||
beforeEach(() => {
|
||||
extension = new MockModelExtension()
|
||||
})
|
||||
|
||||
test('should return the correct extension type', () => {
|
||||
expect(extension.type()).toBe(ExtensionTypeEnum.Model)
|
||||
})
|
||||
|
||||
test('should configure pull options', async () => {
|
||||
const configs = { apiKey: 'test-key', baseUrl: 'https://test-url.com' }
|
||||
const result = await extension.configurePullOptions(configs)
|
||||
expect(result).toEqual(configs)
|
||||
})
|
||||
|
||||
test('should add and get models', async () => {
|
||||
await extension.pullModel('test-model', 'test-id', 'Test Model')
|
||||
|
||||
const models = await extension.getModels()
|
||||
expect(models).toHaveLength(1)
|
||||
expect(models[0].id).toBe('test-id')
|
||||
expect(models[0].name).toBe('Test Model')
|
||||
})
|
||||
|
||||
test('should pull model with default id and name', async () => {
|
||||
await extension.pullModel('test-model')
|
||||
|
||||
const models = await extension.getModels()
|
||||
expect(models).toHaveLength(1)
|
||||
expect(models[0].name).toBe('test-model')
|
||||
})
|
||||
|
||||
test('should cancel model pull', async () => {
|
||||
await extension.pullModel('test-model', 'test-id')
|
||||
|
||||
// Verify model exists
|
||||
let models = await extension.getModels()
|
||||
expect(models).toHaveLength(1)
|
||||
|
||||
// Cancel the pull
|
||||
await extension.cancelModelPull('test-id')
|
||||
|
||||
// Verify model was removed
|
||||
models = await extension.getModels()
|
||||
expect(models).toHaveLength(0)
|
||||
})
|
||||
|
||||
test('should import model', async () => {
|
||||
const optionType: OptionType = {
|
||||
engine: 'test-engine',
|
||||
format: 'test-format',
|
||||
contextLength: 4096
|
||||
}
|
||||
|
||||
await extension.importModel('test-model', '/path/to/model', 'Imported Model', optionType)
|
||||
|
||||
const models = await extension.getModels()
|
||||
expect(models).toHaveLength(1)
|
||||
expect(models[0].name).toBe('Imported Model')
|
||||
expect(models[0].engine).toBe('test-engine')
|
||||
expect(models[0].format).toBe('test-format')
|
||||
expect(models[0].contextLength).toBe(4096)
|
||||
})
|
||||
|
||||
test('should import model with default values', async () => {
|
||||
await extension.importModel('test-model', '/path/to/model')
|
||||
|
||||
const models = await extension.getModels()
|
||||
expect(models).toHaveLength(1)
|
||||
expect(models[0].name).toBe('test-model')
|
||||
expect(models[0].engine).toBe('mock-engine')
|
||||
expect(models[0].format).toBe('mock-format')
|
||||
})
|
||||
|
||||
test('should update model', async () => {
|
||||
await extension.pullModel('test-model', 'test-id', 'Test Model')
|
||||
|
||||
const updatedModel = await extension.updateModel({
|
||||
id: 'test-id',
|
||||
name: 'Updated Model',
|
||||
contextLength: 8192
|
||||
})
|
||||
|
||||
expect(updatedModel.name).toBe('Updated Model')
|
||||
expect(updatedModel.contextLength).toBe(8192)
|
||||
|
||||
// Verify changes persisted
|
||||
const models = await extension.getModels()
|
||||
expect(models[0].name).toBe('Updated Model')
|
||||
expect(models[0].contextLength).toBe(8192)
|
||||
})
|
||||
|
||||
test('should throw error when updating non-existent model', async () => {
|
||||
await expect(extension.updateModel({
|
||||
id: 'non-existent',
|
||||
name: 'Updated Model'
|
||||
})).rejects.toThrow('Model not found')
|
||||
})
|
||||
|
||||
test('should throw error when updating model without ID', async () => {
|
||||
await expect(extension.updateModel({
|
||||
name: 'Updated Model'
|
||||
})).rejects.toThrow('Model ID is required')
|
||||
})
|
||||
|
||||
test('should delete model', async () => {
|
||||
await extension.pullModel('test-model', 'test-id')
|
||||
|
||||
// Verify model exists
|
||||
let models = await extension.getModels()
|
||||
expect(models).toHaveLength(1)
|
||||
|
||||
// Delete the model
|
||||
await extension.deleteModel('test-id')
|
||||
|
||||
// Verify model was removed
|
||||
models = await extension.getModels()
|
||||
expect(models).toHaveLength(0)
|
||||
})
|
||||
|
||||
test('should check if model is loaded', async () => {
|
||||
await extension.pullModel('test-model', 'test-id')
|
||||
|
||||
// Check if model is loaded
|
||||
const isLoaded = await extension.isModelLoaded('test-id')
|
||||
expect(isLoaded).toBe(true)
|
||||
|
||||
// Check if non-existent model is loaded
|
||||
const nonExistentLoaded = await extension.isModelLoaded('non-existent')
|
||||
expect(nonExistentLoaded).toBe(false)
|
||||
})
|
||||
|
||||
test('should add and get sources', async () => {
|
||||
await extension.addSource('https://test-source.com')
|
||||
|
||||
const sources = await extension.getSources()
|
||||
expect(sources).toHaveLength(1)
|
||||
expect(sources[0].url).toBe('https://test-source.com')
|
||||
})
|
||||
|
||||
test('should delete source', async () => {
|
||||
await extension.addSource('https://test-source.com')
|
||||
|
||||
// Get the source ID
|
||||
const sources = await extension.getSources()
|
||||
const sourceId = sources[0].id
|
||||
|
||||
// Delete the source
|
||||
await extension.deleteSource(sourceId)
|
||||
|
||||
// Verify source was removed
|
||||
const updatedSources = await extension.getSources()
|
||||
expect(updatedSources).toHaveLength(0)
|
||||
})
|
||||
})
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@janhq/engine-management-extension",
|
||||
"productName": "Engine Management",
|
||||
"version": "1.0.2",
|
||||
"version": "1.0.3",
|
||||
"description": "Manages AI engines and their configurations.",
|
||||
"main": "dist/index.js",
|
||||
"node": "dist/node/index.cjs.js",
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
},
|
||||
"transform_resp": {
|
||||
"chat_completions": {
|
||||
"template": "{% if input_request.stream %} {\"object\": \"chat.completion.chunk\", \"model\": \"{{ input_request.model }}\", \"choices\": [{\"index\": 0, \"delta\": { {% if input_request.event_type == \"text-generation\" %} \"role\": \"assistant\", \"content\": \"{{ input_request.text }}\" {% else %} \"role\": \"assistant\", \"content\": null {% endif %} }, {% if input_request.event_type == \"stream-end\" %} \"finish_reason\": \"{{ input_request.finish_reason }}\" {% else %} \"finish_reason\": null {% endif %} }]} {% else %} {\"id\": \"{{ input_request.generation_id }}\", \"created\": null, \"object\": \"chat.completion\", \"model\": {% if input_request.model %} \"{{ input_request.model }}\" {% else %} \"command-r-plus-08-2024\" {% endif %}, \"choices\": [{ \"index\": 0, \"message\": { \"role\": \"assistant\", \"content\": {% if not input_request.text %} null {% else %} \"{{ input_request.text }}\" {% endif %}, \"refusal\": null }, \"logprobs\": null, \"finish_reason\": \"{{ input_request.finish_reason }}\" } ], \"usage\": { \"prompt_tokens\": {{ input_request.meta.tokens.input_tokens }}, \"completion_tokens\": {{ input_request.meta.tokens.output_tokens }},\"total_tokens\": {{ input_request.meta.tokens.input_tokens + input_request.meta.tokens.output_tokens }}, \"prompt_tokens_details\": { \"cached_tokens\": 0 },\"completion_tokens_details\": { \"reasoning_tokens\": 0, \"accepted_prediction_tokens\": 0, \"rejected_prediction_tokens\": 0 } }, \"system_fingerprint\": \"fp_6b68a8204b\"} {% endif %}"
|
||||
"template": "{% if input_request.stream %} {\"object\": \"chat.completion.chunk\", \"model\": \"{{ input_request.model }}\", \"choices\": [{\"index\": 0, \"delta\": { {% if input_request.event_type == \"text-generation\" %} \"role\": \"assistant\", \"content\": {{ tojson(input_request.text) }} {% else %} \"role\": \"assistant\", \"content\": null {% endif %} }, {% if input_request.event_type == \"stream-end\" %} \"finish_reason\": \"{{ input_request.finish_reason }}\" {% else %} \"finish_reason\": null {% endif %} }]} {% else %} {\"id\": \"{{ input_request.generation_id }}\", \"created\": null, \"object\": \"chat.completion\", \"model\": {% if input_request.model %} \"{{ input_request.model }}\" {% else %} \"command-r-plus-08-2024\" {% endif %}, \"choices\": [{ \"index\": 0, \"message\": { \"role\": \"assistant\", \"content\": {% if not input_request.text %} null {% else %} {{ tojson(input_request.text) }} {% endif %}, \"refusal\": null }, \"logprobs\": null, \"finish_reason\": \"{{ input_request.finish_reason }}\" } ], \"usage\": { \"prompt_tokens\": {{ input_request.meta.tokens.input_tokens }}, \"completion_tokens\": {{ input_request.meta.tokens.output_tokens }},\"total_tokens\": {{ input_request.meta.tokens.input_tokens + input_request.meta.tokens.output_tokens }}, \"prompt_tokens_details\": { \"cached_tokens\": 0 },\"completion_tokens_details\": { \"reasoning_tokens\": 0, \"accepted_prediction_tokens\": 0, \"rejected_prediction_tokens\": 0 } }, \"system_fingerprint\": \"fp_6b68a8204b\"} {% endif %}"
|
||||
}
|
||||
},
|
||||
"explore_models_url": "https://docs.cohere.com/v2/docs/models"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user