@modelprofile.com/flexharness-providers 8.0.0 → 8.1.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/dist_ts_openai_auth/index.d.ts +1 -3
- package/dist_ts_openai_auth/index.js +2 -7
- package/dist_ts_openai_auth/interfaces.d.ts +24 -0
- package/dist_ts_openai_auth/smartai.connection.openai.d.ts +4 -0
- package/dist_ts_openai_auth/smartai.connection.openai.js +98 -0
- package/package.json +2 -2
- package/readme.md +5 -1
- package/ts_openai_auth/index.ts +1 -9
- package/ts_openai_auth/interfaces.ts +30 -0
- package/ts_openai_auth/readme.md +8 -0
- package/ts_openai_auth/smartai.connection.openai.ts +143 -0
- package/ts_providers/openai/readme.md +5 -0
- package/ts_providers/readme.md +5 -1
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type { IOpenAiModelConnection } from '@modelprofile.com/flexharness-providers/openai';
|
|
2
|
-
import type { IOpenAiChatGptAuthCredentials } from './interfaces.js';
|
|
3
1
|
export * from './interfaces.js';
|
|
4
2
|
export * from './smartai.auth.openai.js';
|
|
3
|
+
export * from './smartai.connection.openai.js';
|
|
5
4
|
export * from './smartai.middleware.openai.js';
|
|
6
|
-
export declare const createOpenAiChatGptModelConnection: (credentials: IOpenAiChatGptAuthCredentials) => IOpenAiModelConnection;
|
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import { createOpenAiChatGptProviderSettings } from './smartai.auth.openai.js';
|
|
2
|
-
import { createOpenAiChatGptInstructionsMiddleware } from './smartai.middleware.openai.js';
|
|
3
1
|
export * from './interfaces.js';
|
|
4
2
|
export * from './smartai.auth.openai.js';
|
|
3
|
+
export * from './smartai.connection.openai.js';
|
|
5
4
|
export * from './smartai.middleware.openai.js';
|
|
6
|
-
|
|
7
|
-
settings: createOpenAiChatGptProviderSettings(credentials),
|
|
8
|
-
middleware: createOpenAiChatGptInstructionsMiddleware(),
|
|
9
|
-
});
|
|
10
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19vcGVuYWlfYXV0aC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQUUsbUNBQW1DLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUMvRSxPQUFPLEVBQUUseUNBQXlDLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUMzRixjQUFjLGlCQUFpQixDQUFDO0FBQ2hDLGNBQWMsMEJBQTBCLENBQUM7QUFDekMsY0FBYyxnQ0FBZ0MsQ0FBQztBQUUvQyxNQUFNLENBQUMsTUFBTSxrQ0FBa0MsR0FBRyxDQUFDLFdBQTBDLEVBQTBCLEVBQUUsQ0FBQyxDQUFDO0lBQ3pILFFBQVEsRUFBRSxtQ0FBbUMsQ0FBQyxXQUFXLENBQUM7SUFDMUQsVUFBVSxFQUFFLHlDQUF5QyxFQUFFO0NBQ3hELENBQUMsQ0FBQyJ9
|
|
5
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90c19vcGVuYWlfYXV0aC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLGlCQUFpQixDQUFDO0FBQ2hDLGNBQWMsMEJBQTBCLENBQUM7QUFDekMsY0FBYyxnQ0FBZ0MsQ0FBQztBQUMvQyxjQUFjLGdDQUFnQyxDQUFDIn0=
|
|
@@ -17,6 +17,30 @@ export interface IOpenAiChatGptAuthCredentials {
|
|
|
17
17
|
tokenInfo?: IOpenAiChatGptTokenInfo;
|
|
18
18
|
originator?: string;
|
|
19
19
|
}
|
|
20
|
+
export interface IOpenAiChatGptAccessIdentity {
|
|
21
|
+
/** Exact workspace claim expected on every resolved access token. */
|
|
22
|
+
readonly accountId: string | undefined;
|
|
23
|
+
/** Exact residency claim expected on every resolved access token. */
|
|
24
|
+
readonly isFedrampAccount: boolean;
|
|
25
|
+
}
|
|
26
|
+
export interface IOpenAiChatGptAccessRequest {
|
|
27
|
+
/** The provider request signal. A resolver should pass it through to its authority. */
|
|
28
|
+
readonly signal: AbortSignal;
|
|
29
|
+
}
|
|
30
|
+
/** Access-only result. Refresh and ID tokens are not part of the request-time contract. */
|
|
31
|
+
export interface IOpenAiChatGptResolvedAccess {
|
|
32
|
+
readonly accessToken: string;
|
|
33
|
+
}
|
|
34
|
+
export interface IOpenAiChatGptDynamicModelConnectionOptions {
|
|
35
|
+
/** Pins the account and residency boundary for the lifetime of the connection. */
|
|
36
|
+
readonly expectedIdentity: IOpenAiChatGptAccessIdentity;
|
|
37
|
+
/** Stable provider originator header. Defaults to `smartai`. */
|
|
38
|
+
readonly originator?: string;
|
|
39
|
+
/** Resolves current access immediately before every provider network request. */
|
|
40
|
+
readonly resolveAccess: (request: IOpenAiChatGptAccessRequest) => Promise<IOpenAiChatGptResolvedAccess>;
|
|
41
|
+
/** Optional underlying transport. Defaults to the current global fetch. */
|
|
42
|
+
readonly fetch?: typeof fetch;
|
|
43
|
+
}
|
|
20
44
|
export interface IOpenAiChatGptTokenData extends IOpenAiChatGptAuthCredentials {
|
|
21
45
|
refreshToken: string;
|
|
22
46
|
tokenInfo: IOpenAiChatGptTokenInfo;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { IOpenAiModelConnection } from '@modelprofile.com/flexharness-providers/openai';
|
|
2
|
+
import type { IOpenAiChatGptAuthCredentials, IOpenAiChatGptDynamicModelConnectionOptions } from './interfaces.js';
|
|
3
|
+
export declare const createOpenAiChatGptModelConnection: (credentialsArg: IOpenAiChatGptAuthCredentials) => IOpenAiModelConnection;
|
|
4
|
+
export declare const createOpenAiChatGptDynamicModelConnection: (optionsArg: IOpenAiChatGptDynamicModelConnectionOptions) => IOpenAiModelConnection;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createOpenAiChatGptProviderSettings, OPENAI_CHATGPT_CODEX_BASE_URL, OpenAiChatGptAuthError, } from './smartai.auth.openai.js';
|
|
2
|
+
import { createOpenAiChatGptInstructionsMiddleware } from './smartai.middleware.openai.js';
|
|
3
|
+
const codexBaseUrl = new URL(OPENAI_CHATGPT_CODEX_BASE_URL);
|
|
4
|
+
function assertCodexRequestUrl(normalizedUrlArg) {
|
|
5
|
+
const url = new URL(normalizedUrlArg);
|
|
6
|
+
const isCodexPath = url.pathname === codexBaseUrl.pathname
|
|
7
|
+
|| url.pathname.startsWith(`${codexBaseUrl.pathname}/`);
|
|
8
|
+
if (url.origin !== codexBaseUrl.origin
|
|
9
|
+
|| url.username !== ''
|
|
10
|
+
|| url.password !== ''
|
|
11
|
+
|| !isCodexPath) {
|
|
12
|
+
throw new OpenAiChatGptAuthError('OpenAI ChatGPT inference URL is outside the Codex endpoint.');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function requireExpectedIdentity(optionsArg) {
|
|
16
|
+
const expectedIdentity = optionsArg.expectedIdentity;
|
|
17
|
+
if (!expectedIdentity
|
|
18
|
+
|| (expectedIdentity.accountId !== undefined
|
|
19
|
+
&& (typeof expectedIdentity.accountId !== 'string' || expectedIdentity.accountId.length === 0))
|
|
20
|
+
|| typeof expectedIdentity.isFedrampAccount !== 'boolean') {
|
|
21
|
+
throw new OpenAiChatGptAuthError('OpenAI ChatGPT expected access identity is invalid.');
|
|
22
|
+
}
|
|
23
|
+
return Object.freeze({
|
|
24
|
+
accountId: expectedIdentity.accountId,
|
|
25
|
+
isFedrampAccount: expectedIdentity.isFedrampAccount,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
function assertExpectedIdentity(settingsArg, expectedIdentityArg) {
|
|
29
|
+
const accountId = settingsArg.headers['ChatGPT-Account-ID'];
|
|
30
|
+
const isFedrampAccount = settingsArg.headers['X-OpenAI-Fedramp'] === 'true';
|
|
31
|
+
if (accountId !== expectedIdentityArg.accountId
|
|
32
|
+
|| isFedrampAccount !== expectedIdentityArg.isFedrampAccount) {
|
|
33
|
+
throw new OpenAiChatGptAuthError('OpenAI ChatGPT resolved access identity does not match the connection.');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function applyAccessHeaders(headersArg, settingsArg) {
|
|
37
|
+
headersArg.set('Authorization', `Bearer ${settingsArg.apiKey}`);
|
|
38
|
+
headersArg.set('originator', settingsArg.headers.originator);
|
|
39
|
+
for (const name of ['ChatGPT-Account-ID', 'X-OpenAI-Fedramp']) {
|
|
40
|
+
const value = settingsArg.headers[name];
|
|
41
|
+
if (value === undefined) {
|
|
42
|
+
headersArg.delete(name);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
headersArg.set(name, value);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export const createOpenAiChatGptModelConnection = (credentialsArg) => ({
|
|
50
|
+
settings: createOpenAiChatGptProviderSettings(credentialsArg),
|
|
51
|
+
middleware: createOpenAiChatGptInstructionsMiddleware(),
|
|
52
|
+
});
|
|
53
|
+
export const createOpenAiChatGptDynamicModelConnection = (optionsArg) => {
|
|
54
|
+
if (!optionsArg || typeof optionsArg.resolveAccess !== 'function') {
|
|
55
|
+
throw new OpenAiChatGptAuthError('OpenAI ChatGPT access resolver is required.');
|
|
56
|
+
}
|
|
57
|
+
const expectedIdentity = requireExpectedIdentity(optionsArg);
|
|
58
|
+
const resolveAccess = optionsArg.resolveAccess;
|
|
59
|
+
const originator = optionsArg.originator;
|
|
60
|
+
const transport = optionsArg.fetch ?? globalThis.fetch;
|
|
61
|
+
if (typeof transport !== 'function') {
|
|
62
|
+
throw new OpenAiChatGptAuthError('fetch is not available for OpenAI ChatGPT inference.');
|
|
63
|
+
}
|
|
64
|
+
const fetchWithCurrentAccess = async (inputArg, initArg) => {
|
|
65
|
+
const request = new Request(inputArg, {
|
|
66
|
+
...initArg,
|
|
67
|
+
redirect: 'error',
|
|
68
|
+
});
|
|
69
|
+
assertCodexRequestUrl(request.url);
|
|
70
|
+
request.signal.throwIfAborted();
|
|
71
|
+
const access = await resolveAccess({ signal: request.signal });
|
|
72
|
+
request.signal.throwIfAborted();
|
|
73
|
+
const settings = createOpenAiChatGptProviderSettings({
|
|
74
|
+
accessToken: access.accessToken,
|
|
75
|
+
originator,
|
|
76
|
+
});
|
|
77
|
+
if (settings.baseURL !== OPENAI_CHATGPT_CODEX_BASE_URL) {
|
|
78
|
+
throw new OpenAiChatGptAuthError('OpenAI ChatGPT inference endpoint is invalid.');
|
|
79
|
+
}
|
|
80
|
+
assertExpectedIdentity(settings, expectedIdentity);
|
|
81
|
+
const headers = new Headers(request.headers);
|
|
82
|
+
applyAccessHeaders(headers, settings);
|
|
83
|
+
const authenticatedRequest = new Request(request, {
|
|
84
|
+
headers,
|
|
85
|
+
});
|
|
86
|
+
authenticatedRequest.signal.throwIfAborted();
|
|
87
|
+
return transport(authenticatedRequest);
|
|
88
|
+
};
|
|
89
|
+
return {
|
|
90
|
+
settings: {
|
|
91
|
+
apiKey: '',
|
|
92
|
+
baseURL: OPENAI_CHATGPT_CODEX_BASE_URL,
|
|
93
|
+
fetch: fetchWithCurrentAccess,
|
|
94
|
+
},
|
|
95
|
+
middleware: createOpenAiChatGptInstructionsMiddleware(),
|
|
96
|
+
};
|
|
97
|
+
};
|
|
98
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic21hcnRhaS5jb25uZWN0aW9uLm9wZW5haS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzX29wZW5haV9hdXRoL3NtYXJ0YWkuY29ubmVjdGlvbi5vcGVuYWkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBS0EsT0FBTyxFQUNMLG1DQUFtQyxFQUNuQyw2QkFBNkIsRUFDN0Isc0JBQXNCLEdBQ3ZCLE1BQU0sMEJBQTBCLENBQUM7QUFDbEMsT0FBTyxFQUFFLHlDQUF5QyxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFFM0YsTUFBTSxZQUFZLEdBQUcsSUFBSSxHQUFHLENBQUMsNkJBQTZCLENBQUMsQ0FBQztBQUU1RCxTQUFTLHFCQUFxQixDQUFDLGdCQUF3QjtJQUNyRCxNQUFNLEdBQUcsR0FBRyxJQUFJLEdBQUcsQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDO0lBQ3RDLE1BQU0sV0FBVyxHQUFHLEdBQUcsQ0FBQyxRQUFRLEtBQUssWUFBWSxDQUFDLFFBQVE7V0FDckQsR0FBRyxDQUFDLFFBQVEsQ0FBQyxVQUFVLENBQUMsR0FBRyxZQUFZLENBQUMsUUFBUSxHQUFHLENBQUMsQ0FBQztJQUMxRCxJQUNFLEdBQUcsQ0FBQyxNQUFNLEtBQUssWUFBWSxDQUFDLE1BQU07V0FDL0IsR0FBRyxDQUFDLFFBQVEsS0FBSyxFQUFFO1dBQ25CLEdBQUcsQ0FBQyxRQUFRLEtBQUssRUFBRTtXQUNuQixDQUFDLFdBQVcsRUFDZixDQUFDO1FBQ0QsTUFBTSxJQUFJLHNCQUFzQixDQUFDLDZEQUE2RCxDQUFDLENBQUM7SUFDbEcsQ0FBQztBQUNILENBQUM7QUFFRCxTQUFTLHVCQUF1QixDQUFDLFVBQXVEO0lBSXRGLE1BQU0sZ0JBQWdCLEdBQUcsVUFBVSxDQUFDLGdCQUFnQixDQUFDO0lBQ3JELElBQ0UsQ0FBQyxnQkFBZ0I7V0FDZCxDQUNELGdCQUFnQixDQUFDLFNBQVMsS0FBSyxTQUFTO2VBQ3JDLENBQUMsT0FBTyxnQkFBZ0IsQ0FBQyxTQUFTLEtBQUss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
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"author": "Task Venture Capital GmbH",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"name": "@modelprofile.com/flexharness-providers",
|
|
5
|
-
"version": "8.
|
|
5
|
+
"version": "8.1.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"description": "Model providers, authentication, account management, media and research integrations in one package.",
|
|
8
8
|
"main": "./dist_ts_providers/index.js",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"@ai-sdk/provider": "^4.0.8",
|
|
76
76
|
"@ai-sdk/xai": "^4.0.44",
|
|
77
77
|
"@anthropic-ai/sdk": "0.95.2",
|
|
78
|
-
"@modelprofile.com/flexharness-models": "8.
|
|
78
|
+
"@modelprofile.com/flexharness-models": "8.1.0",
|
|
79
79
|
"ai": "^7.0.79",
|
|
80
80
|
"openai": "^6.37.0",
|
|
81
81
|
"proper-lockfile": "^4.1.2"
|
package/readme.md
CHANGED
|
@@ -36,7 +36,11 @@ Provider SDKs are installed together. Authentication, file access and vendor med
|
|
|
36
36
|
are separate entrypoints and are not imported by the provider factory root. For
|
|
37
37
|
ChatGPT authentication, pass
|
|
38
38
|
`connection: createOpenAiChatGptModelConnection(credentials)` from `/auth` to the
|
|
39
|
-
OpenAI model options.
|
|
39
|
+
OpenAI model options. Delegated runtimes use
|
|
40
|
+
`createOpenAiChatGptDynamicModelConnection({ expectedIdentity, resolveAccess })`
|
|
41
|
+
instead; it resolves current access at every provider HTTP request while keeping
|
|
42
|
+
account and residency identity fixed for the connection. API-key inference
|
|
43
|
+
needs no authentication setup.
|
|
40
44
|
|
|
41
45
|
The package includes the applicable OpenAI Codex license and third-party notices.
|
|
42
46
|
|
package/ts_openai_auth/index.ts
CHANGED
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
import type { IOpenAiModelConnection } from '@modelprofile.com/flexharness-providers/openai';
|
|
2
|
-
import type { IOpenAiChatGptAuthCredentials } from './interfaces.js';
|
|
3
|
-
import { createOpenAiChatGptProviderSettings } from './smartai.auth.openai.js';
|
|
4
|
-
import { createOpenAiChatGptInstructionsMiddleware } from './smartai.middleware.openai.js';
|
|
5
1
|
export * from './interfaces.js';
|
|
6
2
|
export * from './smartai.auth.openai.js';
|
|
3
|
+
export * from './smartai.connection.openai.js';
|
|
7
4
|
export * from './smartai.middleware.openai.js';
|
|
8
|
-
|
|
9
|
-
export const createOpenAiChatGptModelConnection = (credentials: IOpenAiChatGptAuthCredentials): IOpenAiModelConnection => ({
|
|
10
|
-
settings: createOpenAiChatGptProviderSettings(credentials),
|
|
11
|
-
middleware: createOpenAiChatGptInstructionsMiddleware(),
|
|
12
|
-
});
|
|
@@ -19,6 +19,36 @@ export interface IOpenAiChatGptAuthCredentials {
|
|
|
19
19
|
originator?: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
export interface IOpenAiChatGptAccessIdentity {
|
|
23
|
+
/** Exact workspace claim expected on every resolved access token. */
|
|
24
|
+
readonly accountId: string | undefined;
|
|
25
|
+
/** Exact residency claim expected on every resolved access token. */
|
|
26
|
+
readonly isFedrampAccount: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface IOpenAiChatGptAccessRequest {
|
|
30
|
+
/** The provider request signal. A resolver should pass it through to its authority. */
|
|
31
|
+
readonly signal: AbortSignal;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Access-only result. Refresh and ID tokens are not part of the request-time contract. */
|
|
35
|
+
export interface IOpenAiChatGptResolvedAccess {
|
|
36
|
+
readonly accessToken: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface IOpenAiChatGptDynamicModelConnectionOptions {
|
|
40
|
+
/** Pins the account and residency boundary for the lifetime of the connection. */
|
|
41
|
+
readonly expectedIdentity: IOpenAiChatGptAccessIdentity;
|
|
42
|
+
/** Stable provider originator header. Defaults to `smartai`. */
|
|
43
|
+
readonly originator?: string;
|
|
44
|
+
/** Resolves current access immediately before every provider network request. */
|
|
45
|
+
readonly resolveAccess: (
|
|
46
|
+
request: IOpenAiChatGptAccessRequest,
|
|
47
|
+
) => Promise<IOpenAiChatGptResolvedAccess>;
|
|
48
|
+
/** Optional underlying transport. Defaults to the current global fetch. */
|
|
49
|
+
readonly fetch?: typeof fetch;
|
|
50
|
+
}
|
|
51
|
+
|
|
22
52
|
export interface IOpenAiChatGptTokenData extends IOpenAiChatGptAuthCredentials {
|
|
23
53
|
refreshToken: string;
|
|
24
54
|
tokenInfo: IOpenAiChatGptTokenInfo;
|
package/ts_openai_auth/readme.md
CHANGED
|
@@ -23,6 +23,14 @@ the current access token. The token must contain an unexpired `exp` claim;
|
|
|
23
23
|
supplied `accountId`, `isFedrampAccount`, and `tokenInfo` must agree with its
|
|
24
24
|
claims. A saved ID-token snapshot cannot override current access-token facts.
|
|
25
25
|
|
|
26
|
+
Use `createOpenAiChatGptDynamicModelConnection()` for delegated access owned by
|
|
27
|
+
an external authority. Pin its exact account and FedRAMP identity, then provide
|
|
28
|
+
an async `resolveAccess({ signal })` callback. The connection resolves and
|
|
29
|
+
validates current access immediately before every provider HTTP request, so
|
|
30
|
+
multi-step tool runs do not retain the token that admitted the run. Resolution,
|
|
31
|
+
identity, expiry, endpoint, redirect, and abort failures stop before the
|
|
32
|
+
underlying transport is called.
|
|
33
|
+
|
|
26
34
|
## Issue Reporting and Security
|
|
27
35
|
|
|
28
36
|
Report issues and security concerns through [community.foss.global](https://community.foss.global).
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import type { IOpenAiModelConnection } from '@modelprofile.com/flexharness-providers/openai';
|
|
2
|
+
import type {
|
|
3
|
+
IOpenAiChatGptAuthCredentials,
|
|
4
|
+
IOpenAiChatGptDynamicModelConnectionOptions,
|
|
5
|
+
} from './interfaces.js';
|
|
6
|
+
import {
|
|
7
|
+
createOpenAiChatGptProviderSettings,
|
|
8
|
+
OPENAI_CHATGPT_CODEX_BASE_URL,
|
|
9
|
+
OpenAiChatGptAuthError,
|
|
10
|
+
} from './smartai.auth.openai.js';
|
|
11
|
+
import { createOpenAiChatGptInstructionsMiddleware } from './smartai.middleware.openai.js';
|
|
12
|
+
|
|
13
|
+
const codexBaseUrl = new URL(OPENAI_CHATGPT_CODEX_BASE_URL);
|
|
14
|
+
|
|
15
|
+
function assertCodexRequestUrl(normalizedUrlArg: string): void {
|
|
16
|
+
const url = new URL(normalizedUrlArg);
|
|
17
|
+
const isCodexPath = url.pathname === codexBaseUrl.pathname
|
|
18
|
+
|| url.pathname.startsWith(`${codexBaseUrl.pathname}/`);
|
|
19
|
+
if (
|
|
20
|
+
url.origin !== codexBaseUrl.origin
|
|
21
|
+
|| url.username !== ''
|
|
22
|
+
|| url.password !== ''
|
|
23
|
+
|| !isCodexPath
|
|
24
|
+
) {
|
|
25
|
+
throw new OpenAiChatGptAuthError('OpenAI ChatGPT inference URL is outside the Codex endpoint.');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function requireExpectedIdentity(optionsArg: IOpenAiChatGptDynamicModelConnectionOptions): {
|
|
30
|
+
accountId: string | undefined;
|
|
31
|
+
isFedrampAccount: boolean;
|
|
32
|
+
} {
|
|
33
|
+
const expectedIdentity = optionsArg.expectedIdentity;
|
|
34
|
+
if (
|
|
35
|
+
!expectedIdentity
|
|
36
|
+
|| (
|
|
37
|
+
expectedIdentity.accountId !== undefined
|
|
38
|
+
&& (typeof expectedIdentity.accountId !== 'string' || expectedIdentity.accountId.length === 0)
|
|
39
|
+
)
|
|
40
|
+
|| typeof expectedIdentity.isFedrampAccount !== 'boolean'
|
|
41
|
+
) {
|
|
42
|
+
throw new OpenAiChatGptAuthError('OpenAI ChatGPT expected access identity is invalid.');
|
|
43
|
+
}
|
|
44
|
+
return Object.freeze({
|
|
45
|
+
accountId: expectedIdentity.accountId,
|
|
46
|
+
isFedrampAccount: expectedIdentity.isFedrampAccount,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function assertExpectedIdentity(
|
|
51
|
+
settingsArg: ReturnType<typeof createOpenAiChatGptProviderSettings>,
|
|
52
|
+
expectedIdentityArg: Readonly<{
|
|
53
|
+
accountId: string | undefined;
|
|
54
|
+
isFedrampAccount: boolean;
|
|
55
|
+
}>,
|
|
56
|
+
): void {
|
|
57
|
+
const accountId = settingsArg.headers['ChatGPT-Account-ID'];
|
|
58
|
+
const isFedrampAccount = settingsArg.headers['X-OpenAI-Fedramp'] === 'true';
|
|
59
|
+
if (
|
|
60
|
+
accountId !== expectedIdentityArg.accountId
|
|
61
|
+
|| isFedrampAccount !== expectedIdentityArg.isFedrampAccount
|
|
62
|
+
) {
|
|
63
|
+
throw new OpenAiChatGptAuthError(
|
|
64
|
+
'OpenAI ChatGPT resolved access identity does not match the connection.',
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function applyAccessHeaders(
|
|
70
|
+
headersArg: Headers,
|
|
71
|
+
settingsArg: ReturnType<typeof createOpenAiChatGptProviderSettings>,
|
|
72
|
+
): void {
|
|
73
|
+
headersArg.set('Authorization', `Bearer ${settingsArg.apiKey}`);
|
|
74
|
+
headersArg.set('originator', settingsArg.headers.originator);
|
|
75
|
+
|
|
76
|
+
for (const name of ['ChatGPT-Account-ID', 'X-OpenAI-Fedramp']) {
|
|
77
|
+
const value = settingsArg.headers[name];
|
|
78
|
+
if (value === undefined) {
|
|
79
|
+
headersArg.delete(name);
|
|
80
|
+
} else {
|
|
81
|
+
headersArg.set(name, value);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const createOpenAiChatGptModelConnection = (
|
|
87
|
+
credentialsArg: IOpenAiChatGptAuthCredentials,
|
|
88
|
+
): IOpenAiModelConnection => ({
|
|
89
|
+
settings: createOpenAiChatGptProviderSettings(credentialsArg),
|
|
90
|
+
middleware: createOpenAiChatGptInstructionsMiddleware(),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
export const createOpenAiChatGptDynamicModelConnection = (
|
|
94
|
+
optionsArg: IOpenAiChatGptDynamicModelConnectionOptions,
|
|
95
|
+
): IOpenAiModelConnection => {
|
|
96
|
+
if (!optionsArg || typeof optionsArg.resolveAccess !== 'function') {
|
|
97
|
+
throw new OpenAiChatGptAuthError('OpenAI ChatGPT access resolver is required.');
|
|
98
|
+
}
|
|
99
|
+
const expectedIdentity = requireExpectedIdentity(optionsArg);
|
|
100
|
+
const resolveAccess = optionsArg.resolveAccess;
|
|
101
|
+
const originator = optionsArg.originator;
|
|
102
|
+
const transport = optionsArg.fetch ?? globalThis.fetch;
|
|
103
|
+
if (typeof transport !== 'function') {
|
|
104
|
+
throw new OpenAiChatGptAuthError('fetch is not available for OpenAI ChatGPT inference.');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const fetchWithCurrentAccess: typeof fetch = async (inputArg, initArg) => {
|
|
108
|
+
const request = new Request(inputArg, {
|
|
109
|
+
...initArg,
|
|
110
|
+
redirect: 'error',
|
|
111
|
+
});
|
|
112
|
+
assertCodexRequestUrl(request.url);
|
|
113
|
+
request.signal.throwIfAborted();
|
|
114
|
+
|
|
115
|
+
const access = await resolveAccess({ signal: request.signal });
|
|
116
|
+
request.signal.throwIfAborted();
|
|
117
|
+
const settings = createOpenAiChatGptProviderSettings({
|
|
118
|
+
accessToken: access.accessToken,
|
|
119
|
+
originator,
|
|
120
|
+
});
|
|
121
|
+
if (settings.baseURL !== OPENAI_CHATGPT_CODEX_BASE_URL) {
|
|
122
|
+
throw new OpenAiChatGptAuthError('OpenAI ChatGPT inference endpoint is invalid.');
|
|
123
|
+
}
|
|
124
|
+
assertExpectedIdentity(settings, expectedIdentity);
|
|
125
|
+
|
|
126
|
+
const headers = new Headers(request.headers);
|
|
127
|
+
applyAccessHeaders(headers, settings);
|
|
128
|
+
const authenticatedRequest = new Request(request, {
|
|
129
|
+
headers,
|
|
130
|
+
});
|
|
131
|
+
authenticatedRequest.signal.throwIfAborted();
|
|
132
|
+
return transport(authenticatedRequest);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
settings: {
|
|
137
|
+
apiKey: '',
|
|
138
|
+
baseURL: OPENAI_CHATGPT_CODEX_BASE_URL,
|
|
139
|
+
fetch: fetchWithCurrentAccess,
|
|
140
|
+
},
|
|
141
|
+
middleware: createOpenAiChatGptInstructionsMiddleware(),
|
|
142
|
+
};
|
|
143
|
+
};
|
|
@@ -11,6 +11,11 @@ pnpm add @modelprofile.com/flexharness-providers
|
|
|
11
11
|
See the [toolbox usage and migration guide](https://code.foss.global/modelprofile.com/flexharness#readme)
|
|
12
12
|
for composition examples and the SmartAI/SmartAgent migration map.
|
|
13
13
|
|
|
14
|
+
ChatGPT connections come from the `/auth` subpath. Use the dynamic connection
|
|
15
|
+
factory for delegated authority access so every provider HTTP request resolves
|
|
16
|
+
a current token while the connection retains its pinned account and residency
|
|
17
|
+
identity.
|
|
18
|
+
|
|
14
19
|
## Issue Reporting and Security
|
|
15
20
|
|
|
16
21
|
Report issues and security concerns through [community.foss.global](https://community.foss.global).
|
package/ts_providers/readme.md
CHANGED
|
@@ -36,7 +36,11 @@ Provider SDKs are installed together. Authentication, file access and vendor med
|
|
|
36
36
|
are separate entrypoints and are not imported by the provider factory root. For
|
|
37
37
|
ChatGPT authentication, pass
|
|
38
38
|
`connection: createOpenAiChatGptModelConnection(credentials)` from `/auth` to the
|
|
39
|
-
OpenAI model options.
|
|
39
|
+
OpenAI model options. Delegated runtimes use
|
|
40
|
+
`createOpenAiChatGptDynamicModelConnection({ expectedIdentity, resolveAccess })`
|
|
41
|
+
instead; it resolves current access at every provider HTTP request while keeping
|
|
42
|
+
account and residency identity fixed for the connection. API-key inference
|
|
43
|
+
needs no authentication setup.
|
|
40
44
|
|
|
41
45
|
The package includes the applicable OpenAI Codex license and third-party notices.
|
|
42
46
|
|