feat: add OpenAI OAuth, strict model overrides, and Gmail pull mode
This commit is contained in:
@@ -140,6 +140,44 @@ describe('LlamaCppClient', () => {
|
||||
}]);
|
||||
});
|
||||
|
||||
it('sanitizes web_search tool schema for llama.cpp', async () => {
|
||||
mockFetch.mockResolvedValue({
|
||||
ok: true,
|
||||
json: () => Promise.resolve({
|
||||
choices: [{ message: { content: 'ok' } }],
|
||||
usage: { prompt_tokens: 1, completion_tokens: 1 },
|
||||
}),
|
||||
});
|
||||
|
||||
const client = new LlamaCppClient({
|
||||
endpoint: 'http://localhost:8080',
|
||||
model: 'test-model',
|
||||
});
|
||||
|
||||
await client.chat({
|
||||
messages: [{ role: 'user', content: 'search' }],
|
||||
tools: [{
|
||||
name: 'web_search',
|
||||
description: 'Search',
|
||||
input_schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
query: { type: 'string' },
|
||||
count: { type: 'number' },
|
||||
},
|
||||
required: ['query'],
|
||||
},
|
||||
}],
|
||||
});
|
||||
|
||||
const requestBody = JSON.parse(mockFetch.mock.calls[0][1].body);
|
||||
expect(requestBody.tools[0].function.parameters).toEqual({
|
||||
type: 'object',
|
||||
properties: { query: { type: 'string' } },
|
||||
required: ['query'],
|
||||
});
|
||||
});
|
||||
|
||||
it('parses tool_calls from response', async () => {
|
||||
mockFetch.mockResolvedValue({
|
||||
ok: true,
|
||||
|
||||
@@ -48,6 +48,36 @@ interface LlamaCppStreamChunk {
|
||||
usage?: { prompt_tokens: number; completion_tokens: number };
|
||||
}
|
||||
|
||||
function sanitizeToolParametersForLlamaCpp(toolName: string, parameters: unknown): unknown {
|
||||
// llama.cpp is stricter than most tool-call APIs about JSON schema.
|
||||
// In particular, some builds reject extra optional properties for common tools.
|
||||
// Keep the full schema for most tools, but reduce known-problematic ones.
|
||||
if (toolName !== 'web_search') {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
if (!parameters || typeof parameters !== 'object') {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
const schema = parameters as {
|
||||
type?: unknown;
|
||||
properties?: Record<string, unknown>;
|
||||
required?: unknown;
|
||||
};
|
||||
|
||||
const querySchema = schema.properties?.query;
|
||||
if (!querySchema) {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
return {
|
||||
type: 'object',
|
||||
properties: { query: querySchema },
|
||||
required: ['query'],
|
||||
};
|
||||
}
|
||||
|
||||
/** Message format for OpenAI-compatible chat completions API. */
|
||||
interface LlamaCppChatMessage {
|
||||
role: 'system' | 'user' | 'assistant' | 'tool';
|
||||
@@ -211,7 +241,7 @@ export class LlamaCppClient implements ModelClient {
|
||||
function: {
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
parameters: t.input_schema,
|
||||
parameters: sanitizeToolParametersForLlamaCpp(t.name, t.input_schema),
|
||||
},
|
||||
}));
|
||||
}
|
||||
@@ -292,7 +322,7 @@ export class LlamaCppClient implements ModelClient {
|
||||
function: {
|
||||
name: t.name,
|
||||
description: t.description,
|
||||
parameters: t.input_schema,
|
||||
parameters: sanitizeToolParametersForLlamaCpp(t.name, t.input_schema),
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user