Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/byok-llm-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@ontos-ai/knowhere-sdk": minor
---

Add optional BYOK `llmConfig` on job create, parse, and retrieval query so callers can supply per-request text/vision provider credentials.
50 changes: 50 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,56 @@ const result = await client.parse({
});
```

### Bring Your Own Key (BYOK)

Pass per-request LLM credentials via `llmConfig` on parse/job create and
retrieval queries. Flat root applies to both channels; use `models` for
different model ids on the same endpoint, or `text` / `vision` for different
provider endpoints. Use camelCase in TypeScript; the HTTP client serializes to
snake_case on the wire.

```typescript
// Multimodal shorthand — one model for text + vision
const llmConfig = {
apiKey: process.env.OPENAI_API_KEY,
model: 'gpt-4o',
baseUrl: 'https://api.openai.com/v1',
};

// Same endpoint, different models per channel
const modelsConfig = {
apiKey: process.env.OPENAI_API_KEY,
baseUrl: 'https://api.openai.com/v1',
models: { text: 'gpt-4o-mini', vision: 'gpt-4o' },
};

// Or two different endpoints
const splitLlmConfig = {
text: {
apiKey: process.env.OPENAI_API_KEY,
model: 'gpt-4o-mini',
baseUrl: 'https://api.openai.com/v1',
},
vision: {
apiKey: process.env.DASHSCOPE_API_KEY,
model: 'qwen-vl-max',
baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
},
};

const result = await client.parse({
url: 'https://example.com/doc.pdf',
llmConfig: modelsConfig,
});

const response = await client.retrieval.query({
namespace: 'support-center',
query: 'What is the refund policy?',
useAgentic: true,
llmConfig: splitLlmConfig,
});
```

### Page Citation Assets

Page citation assets are generated by Knowhere during page-memory parsing. The
Expand Down
36 changes: 36 additions & 0 deletions src/__tests__/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,42 @@ describe('Knowhere Client', () => {
);
});

it('should forward llmConfig from parse/startParse to job creation', async () => {
const llmConfig = {
text: {
apiKey: 'sk-text',
model: 'gpt-4o-mini',
baseUrl: 'https://api.openai.com/v1',
},
vision: {
apiKey: 'sk-vision',
model: 'gpt-4o',
},
};

await client.parse({
url: 'https://example.com/doc.pdf',
llmConfig,
});

expect(client.jobs.create).toHaveBeenCalledWith(
expect.objectContaining({
llmConfig,
}),
);

await client.startParse({
url: 'https://example.com/doc.pdf',
llmConfig,
});

expect(client.jobs.create).toHaveBeenLastCalledWith(
expect.objectContaining({
llmConfig,
}),
);
});

it('should handle upload progress callback', async () => {
const progressUpdates: number[] = [];

Expand Down
1 change: 1 addition & 0 deletions src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ export class Knowhere {
documentId: params.documentId,
documentMetadata: params.documentMetadata,
parsingParams: buildParsingParams(params),
llmConfig: params.llmConfig,
webhook,
});

Expand Down
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ export type {
ParsingModel,
DocType,
DocumentMetadata,
LlmConfig,
LlmModelsConfig,
LlmProviderConfig,
WebhookConfig,
CreateJobParams,
UploadParams,
Expand Down
42 changes: 42 additions & 0 deletions src/resources/__tests__/jobs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,48 @@ describe('Jobs Resource', () => {
);
});

it('should include llmConfig when provided', async () => {
mockHttpClient.post.mockResolvedValue({
jobId: 'job-byok',
status: 'pending',
sourceType: 'url',
createdAt: new Date(),
});

await jobs.create({
sourceType: 'url',
sourceUrl: 'https://example.com/doc.pdf',
llmConfig: {
text: {
apiKey: 'sk-text',
model: 'gpt-4o-mini',
baseUrl: 'https://api.openai.com/v1',
},
vision: {
apiKey: 'sk-vision',
model: 'gpt-4o',
},
},
});

expect(mockHttpClient.post).toHaveBeenCalledWith(
'/v2/jobs',
expect.objectContaining({
llmConfig: {
text: {
apiKey: 'sk-text',
model: 'gpt-4o-mini',
baseUrl: 'https://api.openai.com/v1',
},
vision: {
apiKey: 'sk-vision',
model: 'gpt-4o',
},
},
}),
);
});

it('should include webhook configuration', async () => {
mockHttpClient.post.mockResolvedValue({
jobId: 'job-abc',
Expand Down
43 changes: 43 additions & 0 deletions src/resources/__tests__/retrieval.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,49 @@ describe('Retrieval Resource', () => {
});
});

it('should send llmConfig parameter', async () => {
mockHttpClient.post.mockResolvedValue({
namespace: 'default',
query: 'test',
routerUsed: 'workflow_single_step',
answerText: null,
referencedChunks: [],
results: [],
});

await retrieval.query({
query: 'test',
useAgentic: true,
llmConfig: {
text: {
apiKey: 'sk-text',
model: 'gpt-4o-mini',
baseUrl: 'https://api.openai.com/v1',
},
vision: {
apiKey: 'sk-vision',
model: 'gpt-4o',
},
},
});

expect(mockHttpClient.post).toHaveBeenCalledWith('/v2/retrieval/query', {
query: 'test',
useAgentic: true,
llmConfig: {
text: {
apiKey: 'sk-text',
model: 'gpt-4o-mini',
baseUrl: 'https://api.openai.com/v1',
},
vision: {
apiKey: 'sk-vision',
model: 'gpt-4o',
},
},
});
});

it('should send useAgentic parameter', async () => {
mockHttpClient.post.mockResolvedValue({
namespace: 'default',
Expand Down
48 changes: 48 additions & 0 deletions src/types/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,50 @@ export interface WebhookConfig {
url: string;
}

/**
* Per-provider BYOK credentials and routing overrides.
*/
export interface LlmProviderConfig {
/** Provider API key */
apiKey?: string;
/** Model identifier */
model?: string;
/** Provider base URL override */
baseUrl?: string;
}

/**
* Per-channel model ids that share root apiKey / baseUrl.
*/
export interface LlmModelsConfig {
/** Text / planning model id */
text?: string;
/** Vision / VLM model id */
vision?: string;
}

/**
* Bring-your-own-key LLM configuration.
*
* Flat root (`apiKey` / `model` / `baseUrl`) applies to both channels.
* Use `models` for different model ids on the same endpoint, or `text` /
* `vision` objects for different provider endpoints.
*/
export interface LlmConfig {
/** Default provider API key (with model/models + baseUrl) */
apiKey?: string;
/** Default model for both channels (overridden by models.*) */
model?: string;
/** Default OpenAI-compatible base URL */
baseUrl?: string;
/** Per-channel model ids sharing root apiKey / baseUrl */
models?: LlmModelsConfig;
/** Text / chat credentials (replaces root for text) */
text?: LlmProviderConfig;
/** Vision / VLM credentials (replaces root for vision) */
vision?: LlmProviderConfig;
}

/**
* Client-provided display metadata copied onto the published document.
*/
Expand All @@ -68,6 +112,8 @@ export interface CreateJobParams {
documentMetadata?: DocumentMetadata;
/** Parsing configuration */
parsingParams?: ParsingParams;
/** Bring-your-own-key LLM credentials for this job */
llmConfig?: LlmConfig;
/** Webhook configuration */
webhook?: WebhookConfig;
}
Expand Down Expand Up @@ -153,6 +199,8 @@ export interface ParseParams {
* assets into application-owned storage before the result is returned.
*/
storageAdapter?: KnowhereAssetStorageOptions;
/** Bring-your-own-key LLM credentials for this parse */
llmConfig?: LlmConfig;
/** Webhook configuration */
webhook?: WebhookConfig;
/** Upload progress callback */
Expand Down
4 changes: 4 additions & 0 deletions src/types/retrieval.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { LlmConfig } from './params.js';

/**
* Section exclusion for follow-up retrieval queries.
*/
Expand Down Expand Up @@ -60,6 +62,8 @@ export interface RetrievalQueryParams {
excludeDocumentIds?: string[];
/** Document sections to exclude for this request only */
excludeSections?: RetrievalSectionExclusion[];
/** Bring-your-own-key LLM credentials for this query */
llmConfig?: LlmConfig;
}

/**
Expand Down
Loading