@aituber-onair/chat 0.56.0 → 0.57.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ja.md +20 -0
- package/README.md +24 -0
- package/dist/cjs/utils/index.d.ts +1 -0
- package/dist/cjs/utils/index.d.ts.map +1 -1
- package/dist/cjs/utils/index.js +1 -0
- package/dist/cjs/utils/index.js.map +1 -1
- package/dist/cjs/utils/openaiCompatibleEndpoint.d.ts +57 -0
- package/dist/cjs/utils/openaiCompatibleEndpoint.d.ts.map +1 -0
- package/dist/cjs/utils/openaiCompatibleEndpoint.js +193 -0
- package/dist/cjs/utils/openaiCompatibleEndpoint.js.map +1 -0
- package/dist/esm/utils/index.d.ts +1 -0
- package/dist/esm/utils/index.d.ts.map +1 -1
- package/dist/esm/utils/index.js +1 -0
- package/dist/esm/utils/index.js.map +1 -1
- package/dist/esm/utils/openaiCompatibleEndpoint.d.ts +57 -0
- package/dist/esm/utils/openaiCompatibleEndpoint.d.ts.map +1 -0
- package/dist/esm/utils/openaiCompatibleEndpoint.js +186 -0
- package/dist/esm/utils/openaiCompatibleEndpoint.js.map +1 -0
- package/dist/umd/aituber-onair-chat.js +191 -0
- package/dist/umd/aituber-onair-chat.min.js +8 -8
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -460,6 +460,26 @@ const compatibleService = ChatServiceFactory.createChatService(
|
|
|
460
460
|
場合は実行時エラーになります。
|
|
461
461
|
- 既存の `openai` プロバイダーの挙動は変更されません。
|
|
462
462
|
|
|
463
|
+
エンドポイント用ヘルパーは、サーバーの Origin、API のベース URL、完全な Chat Completions URL を受け付けます。クエリとフラグメントを含む URL は受け付けません。プロバイダー本体には引き続き完全な URL を渡してください。
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
import {
|
|
467
|
+
listOpenAICompatibleModels,
|
|
468
|
+
resolveOpenAICompatibleEndpoint,
|
|
469
|
+
testOpenAICompatibleConnection,
|
|
470
|
+
} from '@aituber-onair/chat';
|
|
471
|
+
|
|
472
|
+
const endpoint = resolveOpenAICompatibleEndpoint('http://localhost:11434/v1');
|
|
473
|
+
const models = await listOpenAICompatibleModels({ endpoint: endpoint.baseUrl });
|
|
474
|
+
const connection = await testOpenAICompatibleConnection({
|
|
475
|
+
endpoint: endpoint.baseUrl,
|
|
476
|
+
model: models[0],
|
|
477
|
+
});
|
|
478
|
+
// Pass endpoint.chatCompletionsUrl to the openai-compatible provider.
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
`OPENAI_COMPATIBLE_LOCAL_PRESETS` には Ollama、LM Studio、llama.cpp、vLLM の標準 URL が入っています。モデル一覧は文章を生成せず `GET /models` で取得します。起動方法と CORS 設定は[ローカル LLM ガイド](../../docs/local-llm.ja.md)を参照してください。
|
|
482
|
+
|
|
463
483
|
`reasoning_effort` の選択肢はモデルによって異なります。
|
|
464
484
|
- `gpt-5.6` / `gpt-5.6-sol` / `gpt-5.6-terra` / `gpt-5.6-luna`:
|
|
465
485
|
`'none' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'`
|
package/README.md
CHANGED
|
@@ -478,6 +478,30 @@ Notes:
|
|
|
478
478
|
at runtime.
|
|
479
479
|
- Existing `openai` provider behavior is unchanged.
|
|
480
480
|
|
|
481
|
+
The endpoint helpers accept a server origin, API base URL, or full Chat
|
|
482
|
+
Completions URL. They reject URLs with queries or fragments. The provider
|
|
483
|
+
itself still requires the full endpoint URL.
|
|
484
|
+
|
|
485
|
+
```typescript
|
|
486
|
+
import {
|
|
487
|
+
listOpenAICompatibleModels,
|
|
488
|
+
resolveOpenAICompatibleEndpoint,
|
|
489
|
+
testOpenAICompatibleConnection,
|
|
490
|
+
} from '@aituber-onair/chat';
|
|
491
|
+
|
|
492
|
+
const endpoint = resolveOpenAICompatibleEndpoint('http://localhost:11434/v1');
|
|
493
|
+
const models = await listOpenAICompatibleModels({ endpoint: endpoint.baseUrl });
|
|
494
|
+
const connection = await testOpenAICompatibleConnection({
|
|
495
|
+
endpoint: endpoint.baseUrl,
|
|
496
|
+
model: models[0],
|
|
497
|
+
});
|
|
498
|
+
// Pass endpoint.chatCompletionsUrl to the openai-compatible provider.
|
|
499
|
+
```
|
|
500
|
+
|
|
501
|
+
`OPENAI_COMPATIBLE_LOCAL_PRESETS` contains default URLs for Ollama, LM Studio,
|
|
502
|
+
llama.cpp, and vLLM. Model discovery uses `GET /models` without generating
|
|
503
|
+
tokens. See the [local LLM guide](../../docs/local-llm.md) for setup and CORS.
|
|
504
|
+
|
|
481
505
|
`reasoning_effort` options differ per model:
|
|
482
506
|
- `gpt-5.6` / `gpt-5.6-sol` / `gpt-5.6-terra` / `gpt-5.6-luna`:
|
|
483
507
|
`'none' | 'low' | 'medium' | 'high' | 'xhigh' | 'max'`
|
|
@@ -7,6 +7,7 @@ export * from './screenplay';
|
|
|
7
7
|
export * from './emotionParser';
|
|
8
8
|
export * from './runOnce';
|
|
9
9
|
export * from './openaiCompatibleSse';
|
|
10
|
+
export * from './openaiCompatibleEndpoint';
|
|
10
11
|
export * from './openaiCompatibleTools';
|
|
11
12
|
export * from './processChatFlow';
|
|
12
13
|
export * from './visionModelResolver';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iBAAiB,CAAC"}
|
package/dist/cjs/utils/index.js
CHANGED
|
@@ -23,6 +23,7 @@ __exportStar(require("./screenplay"), exports);
|
|
|
23
23
|
__exportStar(require("./emotionParser"), exports);
|
|
24
24
|
__exportStar(require("./runOnce"), exports);
|
|
25
25
|
__exportStar(require("./openaiCompatibleSse"), exports);
|
|
26
|
+
__exportStar(require("./openaiCompatibleEndpoint"), exports);
|
|
26
27
|
__exportStar(require("./openaiCompatibleTools"), exports);
|
|
27
28
|
__exportStar(require("./processChatFlow"), exports);
|
|
28
29
|
__exportStar(require("./visionModelResolver"), exports);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;GAEG;AACH,0DAAwC;AACxC,0DAAwC;AACxC,+CAA6B;AAC7B,kDAAgC;AAChC,4CAA0B;AAC1B,wDAAsC;AACtC,0DAAwC;AACxC,oDAAkC;AAClC,wDAAsC;AACtC,gEAA8C;AAC9C,kDAAgC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA;;GAEG;AACH,0DAAwC;AACxC,0DAAwC;AACxC,+CAA6B;AAC7B,kDAAgC;AAChC,4CAA0B;AAC1B,wDAAsC;AACtC,6DAA2C;AAC3C,0DAAwC;AACxC,oDAAkC;AAClC,wDAAsC;AACtC,gEAA8C;AAC9C,kDAAgC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** A local server with an OpenAI-compatible Chat Completions endpoint. */
|
|
2
|
+
export type OpenAICompatibleLocalPresetId = 'ollama' | 'lmStudio' | 'llamaCpp' | 'vllm';
|
|
3
|
+
export interface OpenAICompatibleLocalPreset {
|
|
4
|
+
id: OpenAICompatibleLocalPresetId;
|
|
5
|
+
label: string;
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
docsUrl: string;
|
|
8
|
+
corsHint?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Default local URLs. Change the port when your server uses a custom one. */
|
|
11
|
+
export declare const OPENAI_COMPATIBLE_LOCAL_PRESETS: readonly OpenAICompatibleLocalPreset[];
|
|
12
|
+
export type OpenAICompatibleEndpointErrorCode = 'invalid-url' | 'network' | 'aborted' | 'timeout' | 'http' | 'invalid-response';
|
|
13
|
+
/** Error returned by endpoint resolution and model discovery. */
|
|
14
|
+
export declare class OpenAICompatibleEndpointError extends Error {
|
|
15
|
+
readonly code: OpenAICompatibleEndpointErrorCode;
|
|
16
|
+
readonly status?: number;
|
|
17
|
+
constructor(code: OpenAICompatibleEndpointErrorCode, message: string, status?: number);
|
|
18
|
+
}
|
|
19
|
+
export interface ResolvedOpenAICompatibleEndpoint {
|
|
20
|
+
baseUrl: string;
|
|
21
|
+
chatCompletionsUrl: string;
|
|
22
|
+
modelsUrl: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Accepts an origin, API base URL, or full Chat Completions URL.
|
|
26
|
+
* Query strings and fragments are rejected rather than discarded.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveOpenAICompatibleEndpoint(input: string): ResolvedOpenAICompatibleEndpoint;
|
|
29
|
+
export interface ListOpenAICompatibleModelsOptions {
|
|
30
|
+
endpoint: string;
|
|
31
|
+
apiKey?: string;
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
fetch?: typeof fetch;
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Lists model IDs from GET /models, preserving server order.
|
|
38
|
+
* @throws {RangeError} If timeoutMs is not a positive finite number.
|
|
39
|
+
*/
|
|
40
|
+
export declare function listOpenAICompatibleModels(options: ListOpenAICompatibleModelsOptions): Promise<string[]>;
|
|
41
|
+
export type OpenAICompatibleConnectionResult = {
|
|
42
|
+
ok: true;
|
|
43
|
+
latencyMs: number;
|
|
44
|
+
models: string[];
|
|
45
|
+
modelFound?: boolean;
|
|
46
|
+
} | {
|
|
47
|
+
ok: false;
|
|
48
|
+
error: OpenAICompatibleEndpointError;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Tests GET /models without generating tokens.
|
|
52
|
+
* @throws {RangeError} If timeoutMs is not a positive finite number.
|
|
53
|
+
*/
|
|
54
|
+
export declare function testOpenAICompatibleConnection(options: ListOpenAICompatibleModelsOptions & {
|
|
55
|
+
model?: string;
|
|
56
|
+
}): Promise<OpenAICompatibleConnectionResult>;
|
|
57
|
+
//# sourceMappingURL=openaiCompatibleEndpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openaiCompatibleEndpoint.d.ts","sourceRoot":"","sources":["../../../src/utils/openaiCompatibleEndpoint.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,MAAM,6BAA6B,GACrC,QAAQ,GACR,UAAU,GACV,UAAU,GACV,MAAM,CAAC;AAEX,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,6BAA6B,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,8EAA8E;AAC9E,eAAO,MAAM,+BAA+B,EAAE,SAAS,2BAA2B,EAmC/E,CAAC;AAEJ,MAAM,MAAM,iCAAiC,GACzC,aAAa,GACb,SAAS,GACT,SAAS,GACT,SAAS,GACT,MAAM,GACN,kBAAkB,CAAC;AAEvB,iEAAiE;AACjE,qBAAa,6BAA8B,SAAQ,KAAK;IACtD,QAAQ,CAAC,IAAI,EAAE,iCAAiC,CAAC;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAGvB,IAAI,EAAE,iCAAiC,EACvC,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM;CAOlB;AAED,MAAM,WAAW,gCAAgC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,MAAM,GACZ,gCAAgC,CA4ClC;AAED,MAAM,WAAW,iCAAiC;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,iCAAiC,GACzC,OAAO,CAAC,MAAM,EAAE,CAAC,CA2GnB;AAED,MAAM,MAAM,gCAAgC,GACxC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACvE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,6BAA6B,CAAA;CAAE,CAAC;AAExD;;;GAGG;AACH,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,iCAAiC,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9D,OAAO,CAAC,gCAAgC,CAAC,CAyB3C"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OpenAICompatibleEndpointError = exports.OPENAI_COMPATIBLE_LOCAL_PRESETS = void 0;
|
|
4
|
+
exports.resolveOpenAICompatibleEndpoint = resolveOpenAICompatibleEndpoint;
|
|
5
|
+
exports.listOpenAICompatibleModels = listOpenAICompatibleModels;
|
|
6
|
+
exports.testOpenAICompatibleConnection = testOpenAICompatibleConnection;
|
|
7
|
+
/** Default local URLs. Change the port when your server uses a custom one. */
|
|
8
|
+
exports.OPENAI_COMPATIBLE_LOCAL_PRESETS = [
|
|
9
|
+
{
|
|
10
|
+
id: 'ollama',
|
|
11
|
+
label: 'Ollama',
|
|
12
|
+
baseUrl: 'http://localhost:11434/v1',
|
|
13
|
+
docsUrl: 'https://docs.ollama.com/api/openai-compatibility',
|
|
14
|
+
corsHint: 'Set OLLAMA_ORIGINS to allow additional browser origins.',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'lmStudio',
|
|
18
|
+
label: 'LM Studio',
|
|
19
|
+
baseUrl: 'http://localhost:1234/v1',
|
|
20
|
+
docsUrl: 'https://lmstudio.ai/docs/developer/openai-compat',
|
|
21
|
+
corsHint: 'Start the server with lms server start --cors for browser access.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'llamaCpp',
|
|
25
|
+
label: 'llama.cpp',
|
|
26
|
+
baseUrl: 'http://127.0.0.1:8080/v1',
|
|
27
|
+
docsUrl: 'https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md',
|
|
28
|
+
corsHint: 'All origins are allowed by default; use --cors-origins to restrict them.',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: 'vllm',
|
|
32
|
+
label: 'vLLM',
|
|
33
|
+
baseUrl: 'http://localhost:8000/v1',
|
|
34
|
+
docsUrl: 'https://docs.vllm.ai/en/latest/serving/online_serving/openai_compatible_server/',
|
|
35
|
+
corsHint: 'All origins are allowed by default; use --allowed-origins to restrict them.',
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
/** Error returned by endpoint resolution and model discovery. */
|
|
39
|
+
class OpenAICompatibleEndpointError extends Error {
|
|
40
|
+
constructor(code, message, status) {
|
|
41
|
+
super(message);
|
|
42
|
+
this.name = 'OpenAICompatibleEndpointError';
|
|
43
|
+
this.code = code;
|
|
44
|
+
this.status = status;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.OpenAICompatibleEndpointError = OpenAICompatibleEndpointError;
|
|
48
|
+
/**
|
|
49
|
+
* Accepts an origin, API base URL, or full Chat Completions URL.
|
|
50
|
+
* Query strings and fragments are rejected rather than discarded.
|
|
51
|
+
*/
|
|
52
|
+
function resolveOpenAICompatibleEndpoint(input) {
|
|
53
|
+
let url;
|
|
54
|
+
try {
|
|
55
|
+
url = new URL(input.trim());
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
throw new OpenAICompatibleEndpointError('invalid-url', 'Enter a valid HTTP or HTTPS endpoint URL.');
|
|
59
|
+
}
|
|
60
|
+
if ((url.protocol !== 'http:' && url.protocol !== 'https:') ||
|
|
61
|
+
url.search ||
|
|
62
|
+
url.hash ||
|
|
63
|
+
url.username ||
|
|
64
|
+
url.password) {
|
|
65
|
+
throw new OpenAICompatibleEndpointError('invalid-url', 'Use an HTTP or HTTPS endpoint URL without credentials, a query, or a fragment.');
|
|
66
|
+
}
|
|
67
|
+
const path = url.pathname.replace(/\/+$/, '');
|
|
68
|
+
if (path.endsWith('/api/chat') || path.endsWith('/api/generate')) {
|
|
69
|
+
throw new OpenAICompatibleEndpointError('invalid-url', "This looks like Ollama's native API URL. Use the OpenAI-compatible base URL instead, e.g. http://localhost:11434/v1.");
|
|
70
|
+
}
|
|
71
|
+
const basePath = !path || path === '/'
|
|
72
|
+
? '/v1'
|
|
73
|
+
: path.endsWith('/chat/completions')
|
|
74
|
+
? path.slice(0, -'/chat/completions'.length)
|
|
75
|
+
: path;
|
|
76
|
+
const baseUrl = `${url.origin}${basePath}`;
|
|
77
|
+
return {
|
|
78
|
+
baseUrl,
|
|
79
|
+
chatCompletionsUrl: `${baseUrl}/chat/completions`,
|
|
80
|
+
modelsUrl: `${baseUrl}/models`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Lists model IDs from GET /models, preserving server order.
|
|
85
|
+
* @throws {RangeError} If timeoutMs is not a positive finite number.
|
|
86
|
+
*/
|
|
87
|
+
async function listOpenAICompatibleModels(options) {
|
|
88
|
+
const { modelsUrl } = resolveOpenAICompatibleEndpoint(options.endpoint);
|
|
89
|
+
const timeoutMs = options.timeoutMs ?? 8000;
|
|
90
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
91
|
+
throw new RangeError('timeoutMs must be a positive finite number.');
|
|
92
|
+
}
|
|
93
|
+
if (options.signal?.aborted) {
|
|
94
|
+
throw new OpenAICompatibleEndpointError('aborted', 'The models request was aborted by the caller.');
|
|
95
|
+
}
|
|
96
|
+
const controller = new AbortController();
|
|
97
|
+
let abortCause;
|
|
98
|
+
const onAbort = () => {
|
|
99
|
+
if (!abortCause)
|
|
100
|
+
abortCause = 'caller';
|
|
101
|
+
controller.abort();
|
|
102
|
+
};
|
|
103
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
104
|
+
if (options.signal?.aborted)
|
|
105
|
+
onAbort();
|
|
106
|
+
const timeout = setTimeout(() => {
|
|
107
|
+
if (!abortCause)
|
|
108
|
+
abortCause = 'timeout';
|
|
109
|
+
controller.abort();
|
|
110
|
+
}, timeoutMs);
|
|
111
|
+
try {
|
|
112
|
+
const headers = {};
|
|
113
|
+
if (options.apiKey?.trim()) {
|
|
114
|
+
headers.Authorization = `Bearer ${options.apiKey.trim()}`;
|
|
115
|
+
}
|
|
116
|
+
const response = await (options.fetch ?? fetch)(modelsUrl, {
|
|
117
|
+
method: 'GET',
|
|
118
|
+
headers,
|
|
119
|
+
signal: controller.signal,
|
|
120
|
+
});
|
|
121
|
+
if (abortCause) {
|
|
122
|
+
throw new OpenAICompatibleEndpointError('aborted', 'The models request was aborted by the caller.');
|
|
123
|
+
}
|
|
124
|
+
if (!response.ok) {
|
|
125
|
+
throw new OpenAICompatibleEndpointError('http', `Model listing failed with HTTP ${response.status}.`, response.status);
|
|
126
|
+
}
|
|
127
|
+
let body;
|
|
128
|
+
try {
|
|
129
|
+
body = await response.json();
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
throw new OpenAICompatibleEndpointError('invalid-response', 'The models endpoint did not return valid JSON.');
|
|
133
|
+
}
|
|
134
|
+
if (!body ||
|
|
135
|
+
typeof body !== 'object' ||
|
|
136
|
+
!('data' in body) ||
|
|
137
|
+
!Array.isArray(body.data) ||
|
|
138
|
+
!body.data.every((model) => model &&
|
|
139
|
+
typeof model === 'object' &&
|
|
140
|
+
'id' in model &&
|
|
141
|
+
typeof model.id === 'string')) {
|
|
142
|
+
throw new OpenAICompatibleEndpointError('invalid-response', 'The models response must contain data with model IDs.');
|
|
143
|
+
}
|
|
144
|
+
if (abortCause) {
|
|
145
|
+
throw new OpenAICompatibleEndpointError('aborted', 'The models request was aborted by the caller.');
|
|
146
|
+
}
|
|
147
|
+
return [...new Set(body.data.map((model) => model.id))];
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (abortCause === 'caller') {
|
|
151
|
+
throw new OpenAICompatibleEndpointError('aborted', 'The models request was aborted by the caller.');
|
|
152
|
+
}
|
|
153
|
+
if (abortCause === 'timeout') {
|
|
154
|
+
throw new OpenAICompatibleEndpointError('timeout', `The models request timed out after ${timeoutMs} ms.`);
|
|
155
|
+
}
|
|
156
|
+
if (error instanceof OpenAICompatibleEndpointError)
|
|
157
|
+
throw error;
|
|
158
|
+
throw new OpenAICompatibleEndpointError('network', 'Could not reach the models endpoint. In a browser, this is usually a CORS restriction or the server is not running.');
|
|
159
|
+
}
|
|
160
|
+
finally {
|
|
161
|
+
clearTimeout(timeout);
|
|
162
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Tests GET /models without generating tokens.
|
|
167
|
+
* @throws {RangeError} If timeoutMs is not a positive finite number.
|
|
168
|
+
*/
|
|
169
|
+
async function testOpenAICompatibleConnection(options) {
|
|
170
|
+
const startedAt = Date.now();
|
|
171
|
+
try {
|
|
172
|
+
const models = await listOpenAICompatibleModels(options);
|
|
173
|
+
return {
|
|
174
|
+
ok: true,
|
|
175
|
+
latencyMs: Date.now() - startedAt,
|
|
176
|
+
models,
|
|
177
|
+
...(options.model !== undefined
|
|
178
|
+
? { modelFound: models.includes(options.model) }
|
|
179
|
+
: {}),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
if (error instanceof RangeError)
|
|
184
|
+
throw error;
|
|
185
|
+
return {
|
|
186
|
+
ok: false,
|
|
187
|
+
error: error instanceof OpenAICompatibleEndpointError
|
|
188
|
+
? error
|
|
189
|
+
: new OpenAICompatibleEndpointError('network', 'Could not reach the models endpoint. In a browser, this is usually a CORS restriction or the server is not running.'),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=openaiCompatibleEndpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openaiCompatibleEndpoint.js","sourceRoot":"","sources":["../../../src/utils/openaiCompatibleEndpoint.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@ export * from './screenplay';
|
|
|
7
7
|
export * from './emotionParser';
|
|
8
8
|
export * from './runOnce';
|
|
9
9
|
export * from './openaiCompatibleSse';
|
|
10
|
+
export * from './openaiCompatibleEndpoint';
|
|
10
11
|
export * from './openaiCompatibleTools';
|
|
11
12
|
export * from './processChatFlow';
|
|
12
13
|
export * from './visionModelResolver';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iBAAiB,CAAC"}
|
package/dist/esm/utils/index.js
CHANGED
|
@@ -7,6 +7,7 @@ export * from './screenplay';
|
|
|
7
7
|
export * from './emotionParser';
|
|
8
8
|
export * from './runOnce';
|
|
9
9
|
export * from './openaiCompatibleSse';
|
|
10
|
+
export * from './openaiCompatibleEndpoint';
|
|
10
11
|
export * from './openaiCompatibleTools';
|
|
11
12
|
export * from './processChatFlow';
|
|
12
13
|
export * from './visionModelResolver';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iBAAiB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/utils/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,WAAW,CAAC;AAC1B,cAAc,uBAAuB,CAAC;AACtC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,yBAAyB,CAAC;AACxC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/** A local server with an OpenAI-compatible Chat Completions endpoint. */
|
|
2
|
+
export type OpenAICompatibleLocalPresetId = 'ollama' | 'lmStudio' | 'llamaCpp' | 'vllm';
|
|
3
|
+
export interface OpenAICompatibleLocalPreset {
|
|
4
|
+
id: OpenAICompatibleLocalPresetId;
|
|
5
|
+
label: string;
|
|
6
|
+
baseUrl: string;
|
|
7
|
+
docsUrl: string;
|
|
8
|
+
corsHint?: string;
|
|
9
|
+
}
|
|
10
|
+
/** Default local URLs. Change the port when your server uses a custom one. */
|
|
11
|
+
export declare const OPENAI_COMPATIBLE_LOCAL_PRESETS: readonly OpenAICompatibleLocalPreset[];
|
|
12
|
+
export type OpenAICompatibleEndpointErrorCode = 'invalid-url' | 'network' | 'aborted' | 'timeout' | 'http' | 'invalid-response';
|
|
13
|
+
/** Error returned by endpoint resolution and model discovery. */
|
|
14
|
+
export declare class OpenAICompatibleEndpointError extends Error {
|
|
15
|
+
readonly code: OpenAICompatibleEndpointErrorCode;
|
|
16
|
+
readonly status?: number;
|
|
17
|
+
constructor(code: OpenAICompatibleEndpointErrorCode, message: string, status?: number);
|
|
18
|
+
}
|
|
19
|
+
export interface ResolvedOpenAICompatibleEndpoint {
|
|
20
|
+
baseUrl: string;
|
|
21
|
+
chatCompletionsUrl: string;
|
|
22
|
+
modelsUrl: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Accepts an origin, API base URL, or full Chat Completions URL.
|
|
26
|
+
* Query strings and fragments are rejected rather than discarded.
|
|
27
|
+
*/
|
|
28
|
+
export declare function resolveOpenAICompatibleEndpoint(input: string): ResolvedOpenAICompatibleEndpoint;
|
|
29
|
+
export interface ListOpenAICompatibleModelsOptions {
|
|
30
|
+
endpoint: string;
|
|
31
|
+
apiKey?: string;
|
|
32
|
+
timeoutMs?: number;
|
|
33
|
+
fetch?: typeof fetch;
|
|
34
|
+
signal?: AbortSignal;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Lists model IDs from GET /models, preserving server order.
|
|
38
|
+
* @throws {RangeError} If timeoutMs is not a positive finite number.
|
|
39
|
+
*/
|
|
40
|
+
export declare function listOpenAICompatibleModels(options: ListOpenAICompatibleModelsOptions): Promise<string[]>;
|
|
41
|
+
export type OpenAICompatibleConnectionResult = {
|
|
42
|
+
ok: true;
|
|
43
|
+
latencyMs: number;
|
|
44
|
+
models: string[];
|
|
45
|
+
modelFound?: boolean;
|
|
46
|
+
} | {
|
|
47
|
+
ok: false;
|
|
48
|
+
error: OpenAICompatibleEndpointError;
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Tests GET /models without generating tokens.
|
|
52
|
+
* @throws {RangeError} If timeoutMs is not a positive finite number.
|
|
53
|
+
*/
|
|
54
|
+
export declare function testOpenAICompatibleConnection(options: ListOpenAICompatibleModelsOptions & {
|
|
55
|
+
model?: string;
|
|
56
|
+
}): Promise<OpenAICompatibleConnectionResult>;
|
|
57
|
+
//# sourceMappingURL=openaiCompatibleEndpoint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openaiCompatibleEndpoint.d.ts","sourceRoot":"","sources":["../../../src/utils/openaiCompatibleEndpoint.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,MAAM,6BAA6B,GACrC,QAAQ,GACR,UAAU,GACV,UAAU,GACV,MAAM,CAAC;AAEX,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,6BAA6B,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,8EAA8E;AAC9E,eAAO,MAAM,+BAA+B,EAAE,SAAS,2BAA2B,EAmC/E,CAAC;AAEJ,MAAM,MAAM,iCAAiC,GACzC,aAAa,GACb,SAAS,GACT,SAAS,GACT,SAAS,GACT,MAAM,GACN,kBAAkB,CAAC;AAEvB,iEAAiE;AACjE,qBAAa,6BAA8B,SAAQ,KAAK;IACtD,QAAQ,CAAC,IAAI,EAAE,iCAAiC,CAAC;IACjD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;gBAGvB,IAAI,EAAE,iCAAiC,EACvC,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,MAAM;CAOlB;AAED,MAAM,WAAW,gCAAgC;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,KAAK,EAAE,MAAM,GACZ,gCAAgC,CA4ClC;AAED,MAAM,WAAW,iCAAiC;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,iCAAiC,GACzC,OAAO,CAAC,MAAM,EAAE,CAAC,CA2GnB;AAED,MAAM,MAAM,gCAAgC,GACxC;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,OAAO,CAAA;CAAE,GACvE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,6BAA6B,CAAA;CAAE,CAAC;AAExD;;;GAGG;AACH,wBAAsB,8BAA8B,CAClD,OAAO,EAAE,iCAAiC,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC9D,OAAO,CAAC,gCAAgC,CAAC,CAyB3C"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/** Default local URLs. Change the port when your server uses a custom one. */
|
|
2
|
+
export const OPENAI_COMPATIBLE_LOCAL_PRESETS = [
|
|
3
|
+
{
|
|
4
|
+
id: 'ollama',
|
|
5
|
+
label: 'Ollama',
|
|
6
|
+
baseUrl: 'http://localhost:11434/v1',
|
|
7
|
+
docsUrl: 'https://docs.ollama.com/api/openai-compatibility',
|
|
8
|
+
corsHint: 'Set OLLAMA_ORIGINS to allow additional browser origins.',
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
id: 'lmStudio',
|
|
12
|
+
label: 'LM Studio',
|
|
13
|
+
baseUrl: 'http://localhost:1234/v1',
|
|
14
|
+
docsUrl: 'https://lmstudio.ai/docs/developer/openai-compat',
|
|
15
|
+
corsHint: 'Start the server with lms server start --cors for browser access.',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'llamaCpp',
|
|
19
|
+
label: 'llama.cpp',
|
|
20
|
+
baseUrl: 'http://127.0.0.1:8080/v1',
|
|
21
|
+
docsUrl: 'https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md',
|
|
22
|
+
corsHint: 'All origins are allowed by default; use --cors-origins to restrict them.',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: 'vllm',
|
|
26
|
+
label: 'vLLM',
|
|
27
|
+
baseUrl: 'http://localhost:8000/v1',
|
|
28
|
+
docsUrl: 'https://docs.vllm.ai/en/latest/serving/online_serving/openai_compatible_server/',
|
|
29
|
+
corsHint: 'All origins are allowed by default; use --allowed-origins to restrict them.',
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
/** Error returned by endpoint resolution and model discovery. */
|
|
33
|
+
export class OpenAICompatibleEndpointError extends Error {
|
|
34
|
+
constructor(code, message, status) {
|
|
35
|
+
super(message);
|
|
36
|
+
this.name = 'OpenAICompatibleEndpointError';
|
|
37
|
+
this.code = code;
|
|
38
|
+
this.status = status;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Accepts an origin, API base URL, or full Chat Completions URL.
|
|
43
|
+
* Query strings and fragments are rejected rather than discarded.
|
|
44
|
+
*/
|
|
45
|
+
export function resolveOpenAICompatibleEndpoint(input) {
|
|
46
|
+
let url;
|
|
47
|
+
try {
|
|
48
|
+
url = new URL(input.trim());
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
throw new OpenAICompatibleEndpointError('invalid-url', 'Enter a valid HTTP or HTTPS endpoint URL.');
|
|
52
|
+
}
|
|
53
|
+
if ((url.protocol !== 'http:' && url.protocol !== 'https:') ||
|
|
54
|
+
url.search ||
|
|
55
|
+
url.hash ||
|
|
56
|
+
url.username ||
|
|
57
|
+
url.password) {
|
|
58
|
+
throw new OpenAICompatibleEndpointError('invalid-url', 'Use an HTTP or HTTPS endpoint URL without credentials, a query, or a fragment.');
|
|
59
|
+
}
|
|
60
|
+
const path = url.pathname.replace(/\/+$/, '');
|
|
61
|
+
if (path.endsWith('/api/chat') || path.endsWith('/api/generate')) {
|
|
62
|
+
throw new OpenAICompatibleEndpointError('invalid-url', "This looks like Ollama's native API URL. Use the OpenAI-compatible base URL instead, e.g. http://localhost:11434/v1.");
|
|
63
|
+
}
|
|
64
|
+
const basePath = !path || path === '/'
|
|
65
|
+
? '/v1'
|
|
66
|
+
: path.endsWith('/chat/completions')
|
|
67
|
+
? path.slice(0, -'/chat/completions'.length)
|
|
68
|
+
: path;
|
|
69
|
+
const baseUrl = `${url.origin}${basePath}`;
|
|
70
|
+
return {
|
|
71
|
+
baseUrl,
|
|
72
|
+
chatCompletionsUrl: `${baseUrl}/chat/completions`,
|
|
73
|
+
modelsUrl: `${baseUrl}/models`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Lists model IDs from GET /models, preserving server order.
|
|
78
|
+
* @throws {RangeError} If timeoutMs is not a positive finite number.
|
|
79
|
+
*/
|
|
80
|
+
export async function listOpenAICompatibleModels(options) {
|
|
81
|
+
const { modelsUrl } = resolveOpenAICompatibleEndpoint(options.endpoint);
|
|
82
|
+
const timeoutMs = options.timeoutMs ?? 8000;
|
|
83
|
+
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
84
|
+
throw new RangeError('timeoutMs must be a positive finite number.');
|
|
85
|
+
}
|
|
86
|
+
if (options.signal?.aborted) {
|
|
87
|
+
throw new OpenAICompatibleEndpointError('aborted', 'The models request was aborted by the caller.');
|
|
88
|
+
}
|
|
89
|
+
const controller = new AbortController();
|
|
90
|
+
let abortCause;
|
|
91
|
+
const onAbort = () => {
|
|
92
|
+
if (!abortCause)
|
|
93
|
+
abortCause = 'caller';
|
|
94
|
+
controller.abort();
|
|
95
|
+
};
|
|
96
|
+
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
97
|
+
if (options.signal?.aborted)
|
|
98
|
+
onAbort();
|
|
99
|
+
const timeout = setTimeout(() => {
|
|
100
|
+
if (!abortCause)
|
|
101
|
+
abortCause = 'timeout';
|
|
102
|
+
controller.abort();
|
|
103
|
+
}, timeoutMs);
|
|
104
|
+
try {
|
|
105
|
+
const headers = {};
|
|
106
|
+
if (options.apiKey?.trim()) {
|
|
107
|
+
headers.Authorization = `Bearer ${options.apiKey.trim()}`;
|
|
108
|
+
}
|
|
109
|
+
const response = await (options.fetch ?? fetch)(modelsUrl, {
|
|
110
|
+
method: 'GET',
|
|
111
|
+
headers,
|
|
112
|
+
signal: controller.signal,
|
|
113
|
+
});
|
|
114
|
+
if (abortCause) {
|
|
115
|
+
throw new OpenAICompatibleEndpointError('aborted', 'The models request was aborted by the caller.');
|
|
116
|
+
}
|
|
117
|
+
if (!response.ok) {
|
|
118
|
+
throw new OpenAICompatibleEndpointError('http', `Model listing failed with HTTP ${response.status}.`, response.status);
|
|
119
|
+
}
|
|
120
|
+
let body;
|
|
121
|
+
try {
|
|
122
|
+
body = await response.json();
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
throw new OpenAICompatibleEndpointError('invalid-response', 'The models endpoint did not return valid JSON.');
|
|
126
|
+
}
|
|
127
|
+
if (!body ||
|
|
128
|
+
typeof body !== 'object' ||
|
|
129
|
+
!('data' in body) ||
|
|
130
|
+
!Array.isArray(body.data) ||
|
|
131
|
+
!body.data.every((model) => model &&
|
|
132
|
+
typeof model === 'object' &&
|
|
133
|
+
'id' in model &&
|
|
134
|
+
typeof model.id === 'string')) {
|
|
135
|
+
throw new OpenAICompatibleEndpointError('invalid-response', 'The models response must contain data with model IDs.');
|
|
136
|
+
}
|
|
137
|
+
if (abortCause) {
|
|
138
|
+
throw new OpenAICompatibleEndpointError('aborted', 'The models request was aborted by the caller.');
|
|
139
|
+
}
|
|
140
|
+
return [...new Set(body.data.map((model) => model.id))];
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (abortCause === 'caller') {
|
|
144
|
+
throw new OpenAICompatibleEndpointError('aborted', 'The models request was aborted by the caller.');
|
|
145
|
+
}
|
|
146
|
+
if (abortCause === 'timeout') {
|
|
147
|
+
throw new OpenAICompatibleEndpointError('timeout', `The models request timed out after ${timeoutMs} ms.`);
|
|
148
|
+
}
|
|
149
|
+
if (error instanceof OpenAICompatibleEndpointError)
|
|
150
|
+
throw error;
|
|
151
|
+
throw new OpenAICompatibleEndpointError('network', 'Could not reach the models endpoint. In a browser, this is usually a CORS restriction or the server is not running.');
|
|
152
|
+
}
|
|
153
|
+
finally {
|
|
154
|
+
clearTimeout(timeout);
|
|
155
|
+
options.signal?.removeEventListener('abort', onAbort);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Tests GET /models without generating tokens.
|
|
160
|
+
* @throws {RangeError} If timeoutMs is not a positive finite number.
|
|
161
|
+
*/
|
|
162
|
+
export async function testOpenAICompatibleConnection(options) {
|
|
163
|
+
const startedAt = Date.now();
|
|
164
|
+
try {
|
|
165
|
+
const models = await listOpenAICompatibleModels(options);
|
|
166
|
+
return {
|
|
167
|
+
ok: true,
|
|
168
|
+
latencyMs: Date.now() - startedAt,
|
|
169
|
+
models,
|
|
170
|
+
...(options.model !== undefined
|
|
171
|
+
? { modelFound: models.includes(options.model) }
|
|
172
|
+
: {}),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
if (error instanceof RangeError)
|
|
177
|
+
throw error;
|
|
178
|
+
return {
|
|
179
|
+
ok: false,
|
|
180
|
+
error: error instanceof OpenAICompatibleEndpointError
|
|
181
|
+
? error
|
|
182
|
+
: new OpenAICompatibleEndpointError('network', 'Could not reach the models endpoint. In a browser, this is usually a CORS restriction or the server is not running.'),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=openaiCompatibleEndpoint.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openaiCompatibleEndpoint.js","sourceRoot":"","sources":["../../../src/utils/openaiCompatibleEndpoint.ts"],"names":[],"mappings":"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"}
|