test(llm): cover the account listing and gate through the Loader composition
This commit is contained in:
1 parent
7653ca66ab
commit
7755d97dba
2 files changed
+36
-1
No files matched your search
@@ -392,6 +392,27 @@ describe('Cordis provider composition', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('serves the account listing through the Loader composition and refuses models this build cannot serve', async () => {
|
||||
const { ctx, http } = await boot(undefined, { data: [
|
||||
{
|
||||
id: 'deepseek-flash', name: 'DeepSeek-V4.1-Flash', context_window: 1_048_576, max_output_tokens: 100_000,
|
||||
input_modalities: ['text', 'image'], output_modalities: ['text'],
|
||||
api_capabilities: { anthropic_messages: { system_prompt_update: 'in-history' } },
|
||||
},
|
||||
{
|
||||
id: 'deepseek-v5', name: 'DeepSeek-V5', api_capabilities: { anthropic_messages: {} },
|
||||
client_requirements: { 'deepseek-harness': { min_version: '99.0.0' } },
|
||||
},
|
||||
] })
|
||||
ctx.provide('deepseekAccount', { resolveToken: async (_url: string): Promise<string | undefined> => 'fixture-token',
|
||||
rejectToken: async (_token: string): Promise<void> => {} } as DeepSeekAccount)
|
||||
expect((await ctx.llm.listModels('deepseek-account')).map(model => [model.id, model.name])).toEqual([['deepseek-flash', 'DeepSeek-V4.1-Flash']])
|
||||
await expect(ctx.llm.prepareCall({ provider: 'deepseek-account', model: 'deepseek-v5' }))
|
||||
.rejects.toMatchObject({ code: 'CLIENT_UPGRADE_REQUIRED' })
|
||||
await assemble(ctx.llm.stream(options({ provider: 'deepseek-account', model: 'deepseek-flash' })), 'deepseek-flash')
|
||||
expect(http.requests.map(request => [request.path, request.body.max_tokens])).toEqual([['/anthropic/v1/messages', 100_000]])
|
||||
})
|
||||
|
||||
it('does not remove account credentials for HTTP 403', async () => {
|
||||
const { ctx } = await boot((response) => {
|
||||
response.writeHead(403)
|
||||
|
||||
@@ -46,12 +46,26 @@ export function adapter(config: Config = {}) {
|
||||
const files = new DeepSeekFileStore()
|
||||
return new DeepSeekAdapter({ options: () => resolveAdapterOptions(config), resolveAuth: () => Promise.resolve({ headers: { 'x-api-key': 'test-key' } }), resolveUserId: () => 'test-user' as AnonymousUserId, resolveAttachments: () => undefined, resolveImageAccess: () => undefined, resolveFiles: () => files, prepareExtensions })
|
||||
}
|
||||
export async function server(reply: (response: ServerResponse, count: number) => void = response => response.end(sse(textEvents))) {
|
||||
/**
|
||||
* Loopback Messages endpoint recording each model request.
|
||||
* @param reply - answers each Messages request.
|
||||
* @param listing - JSON body served for `GET /models`; omission leaves that request unanswered like any other.
|
||||
* @returns the endpoint base URL, recorded requests, and teardown.
|
||||
*/
|
||||
export async function server(
|
||||
reply: (response: ServerResponse, count: number) => void = response => response.end(sse(textEvents)),
|
||||
listing?: unknown,
|
||||
) {
|
||||
const requests: { path: string; headers: IncomingHttpHeaders; body: Record<string, unknown> }[] = []
|
||||
const http = createServer((request, response) => {
|
||||
void handle(request, response).catch((error: unknown) => response.destroy(error as Error))
|
||||
})
|
||||
async function handle(request: import('node:http').IncomingMessage, response: ServerResponse) {
|
||||
if (listing !== undefined && request.method === 'GET' && request.url === '/models') {
|
||||
response.setHeader('content-type', 'application/json')
|
||||
response.end(JSON.stringify(listing))
|
||||
return
|
||||
}
|
||||
const parts: Buffer[] = []
|
||||
for await (const part of request as AsyncIterable<Buffer>) parts.push(part)
|
||||
requests.push({ path: request.url!, headers: request.headers, body: object(JSON.parse(Buffer.concat(parts).toString())) })
|
||||
|
||||
Reference in new issue
Block a user