@kernl-sdk/ai 0.4.4 → 0.4.6
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +13 -0
- package/dist/language-model.d.ts +7 -2
- package/dist/language-model.d.ts.map +1 -1
- package/dist/language-model.js +13 -5
- package/dist/oauth/openai.d.ts.map +1 -1
- package/dist/oauth/openai.js +2 -30
- package/dist/providers/anthropic.d.ts +2 -19
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +2 -17
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +5 -1
- package/package.json +2 -2
- package/src/language-model.ts +15 -5
- package/src/oauth/openai.ts +4 -39
- package/src/providers/anthropic.ts +3 -24
- package/src/providers/openai.ts +7 -1
- package/dist/oauth/anthropic.d.ts +0 -8
- package/dist/oauth/anthropic.d.ts.map +0 -1
- package/dist/oauth/anthropic.js +0 -65
- package/src/oauth/anthropic.ts +0 -87
package/.turbo/turbo-build.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @kernl/ai
|
|
2
2
|
|
|
3
|
+
## 0.4.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [296c377]
|
|
8
|
+
- kernl@0.12.4
|
|
9
|
+
|
|
10
|
+
## 0.4.5
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 098dd36: Fix OpenAI OAuth by passing `store: false` via model settings. AISDKLanguageModel now accepts optional default settings that are merged with per-request settings. Remove Anthropic OAuth support (blocked server-side).
|
|
15
|
+
|
|
3
16
|
## 0.4.4
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
package/dist/language-model.d.ts
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import type { LanguageModelV3 } from "@ai-sdk/provider";
|
|
2
|
-
import type { LanguageModel, LanguageModelRequest, LanguageModelResponse, LanguageModelStreamEvent } from "@kernl-sdk/protocol";
|
|
2
|
+
import type { LanguageModel, LanguageModelRequest, LanguageModelRequestSettings, LanguageModelResponse, LanguageModelStreamEvent } from "@kernl-sdk/protocol";
|
|
3
3
|
/**
|
|
4
4
|
* LanguageModel adapter for the AI SDK LanguageModelV3.
|
|
5
5
|
*/
|
|
6
6
|
export declare class AISDKLanguageModel implements LanguageModel {
|
|
7
7
|
private model;
|
|
8
|
+
private settings?;
|
|
8
9
|
readonly spec: "1.0";
|
|
9
10
|
readonly provider: string;
|
|
10
11
|
readonly modelId: string;
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* @param model - The underlying AI SDK model
|
|
14
|
+
* @param settings - Default settings to apply to every request (overridden by per-request settings)
|
|
15
|
+
*/
|
|
16
|
+
constructor(model: LanguageModelV3, settings?: Partial<LanguageModelRequestSettings> | undefined);
|
|
12
17
|
/**
|
|
13
18
|
* Get a response from the model.
|
|
14
19
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"language-model.d.ts","sourceRoot":"","sources":["../src/language-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAU7B;;GAEG;AACH,qBAAa,kBAAmB,YAAW,aAAa;
|
|
1
|
+
{"version":3,"file":"language-model.d.ts","sourceRoot":"","sources":["../src/language-model.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAExD,OAAO,KAAK,EACV,aAAa,EACb,oBAAoB,EACpB,4BAA4B,EAC5B,qBAAqB,EACrB,wBAAwB,EACzB,MAAM,qBAAqB,CAAC;AAU7B;;GAEG;AACH,qBAAa,kBAAmB,YAAW,aAAa;IAUpD,OAAO,CAAC,KAAK;IACb,OAAO,CAAC,QAAQ,CAAC;IAVnB,QAAQ,CAAC,IAAI,EAAG,KAAK,CAAU;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB;;;OAGG;gBAEO,KAAK,EAAE,eAAe,EACtB,QAAQ,CAAC,EAAE,OAAO,CAAC,4BAA4B,CAAC,YAAA;IAM1D;;OAEG;IACG,QAAQ,CACZ,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,qBAAqB,CAAC;IAkBjC;;OAEG;IACI,MAAM,CACX,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAAC,wBAAwB,CAAC;CAmF3C"}
|
package/dist/language-model.js
CHANGED
|
@@ -10,11 +10,17 @@ import { normalizeProvider } from "./util.js";
|
|
|
10
10
|
*/
|
|
11
11
|
export class AISDKLanguageModel {
|
|
12
12
|
model;
|
|
13
|
+
settings;
|
|
13
14
|
spec = "1.0";
|
|
14
15
|
provider;
|
|
15
16
|
modelId;
|
|
16
|
-
|
|
17
|
+
/**
|
|
18
|
+
* @param model - The underlying AI SDK model
|
|
19
|
+
* @param settings - Default settings to apply to every request (overridden by per-request settings)
|
|
20
|
+
*/
|
|
21
|
+
constructor(model, settings) {
|
|
17
22
|
this.model = model;
|
|
23
|
+
this.settings = settings;
|
|
18
24
|
this.provider = normalizeProvider(model.provider);
|
|
19
25
|
this.modelId = model.modelId;
|
|
20
26
|
}
|
|
@@ -24,14 +30,15 @@ export class AISDKLanguageModel {
|
|
|
24
30
|
async generate(request) {
|
|
25
31
|
const messages = request.input.map(MESSAGE.encode);
|
|
26
32
|
const tools = request.tools ? request.tools.map(TOOL.encode) : undefined;
|
|
27
|
-
const
|
|
33
|
+
const merged = { ...this.settings, ...request.settings };
|
|
34
|
+
const settings = MODEL_SETTINGS.encode(merged);
|
|
28
35
|
const responseFormat = RESPONSE_FORMAT.encode(request.responseType);
|
|
29
36
|
const result = await this.model.doGenerate({
|
|
30
37
|
prompt: messages,
|
|
31
38
|
tools,
|
|
32
|
-
...settings,
|
|
33
39
|
responseFormat,
|
|
34
40
|
abortSignal: request.abort,
|
|
41
|
+
...settings,
|
|
35
42
|
});
|
|
36
43
|
return MODEL_RESPONSE.decode(result);
|
|
37
44
|
}
|
|
@@ -41,14 +48,15 @@ export class AISDKLanguageModel {
|
|
|
41
48
|
async *stream(request) {
|
|
42
49
|
const messages = request.input.map(MESSAGE.encode);
|
|
43
50
|
const tools = request.tools ? request.tools.map(TOOL.encode) : undefined;
|
|
44
|
-
const
|
|
51
|
+
const merged = { ...this.settings, ...request.settings };
|
|
52
|
+
const settings = MODEL_SETTINGS.encode(merged);
|
|
45
53
|
const responseFormat = RESPONSE_FORMAT.encode(request.responseType);
|
|
46
54
|
const stream = await this.model.doStream({
|
|
47
55
|
prompt: messages,
|
|
48
56
|
tools,
|
|
49
|
-
...settings,
|
|
50
57
|
responseFormat,
|
|
51
58
|
abortSignal: request.abort,
|
|
59
|
+
...settings,
|
|
52
60
|
});
|
|
53
61
|
// text + reasoning buffers for delta accumulation
|
|
54
62
|
const tbuf = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/oauth/openai.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/oauth/openai.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AA2CtD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,sBAAsB,IAE1D,OAAO,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,OAAO,WAAW,KACjB,OAAO,CAAC,QAAQ,CAAC,CAyCrB"}
|
package/dist/oauth/openai.js
CHANGED
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
import { appendFileSync } from "node:fs";
|
|
2
1
|
const TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
3
2
|
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
4
3
|
const CODEX_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
|
|
5
|
-
function debug(msg) {
|
|
6
|
-
try {
|
|
7
|
-
appendFileSync("/tmp/popcorn-debug.log", `${new Date().toISOString()} [oauth/fetch] ${msg}\n`);
|
|
8
|
-
}
|
|
9
|
-
catch {
|
|
10
|
-
// ignore
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
4
|
/**
|
|
14
5
|
* Refresh OpenAI OAuth tokens.
|
|
15
6
|
*/
|
|
@@ -52,23 +43,12 @@ export function createOAuthFetch(creds) {
|
|
|
52
43
|
if (creds.accountId) {
|
|
53
44
|
headers.set("ChatGPT-Account-Id", creds.accountId);
|
|
54
45
|
}
|
|
55
|
-
// Debug: log request
|
|
56
|
-
const url = typeof input === "string"
|
|
57
|
-
? input
|
|
58
|
-
: input instanceof URL
|
|
59
|
-
? input.toString()
|
|
60
|
-
: input.url;
|
|
61
|
-
debug(`Request to: ${url}`);
|
|
62
|
-
debug(`Redirecting to: ${CODEX_ENDPOINT}`);
|
|
63
46
|
// Transform request body for Codex API
|
|
64
47
|
// Codex requires "instructions" field instead of developer/system role in input
|
|
65
|
-
// Codex also requires "store: false"
|
|
66
48
|
let body = init?.body;
|
|
67
49
|
if (body && typeof body === "string") {
|
|
68
50
|
try {
|
|
69
51
|
const parsed = JSON.parse(body);
|
|
70
|
-
// Codex requires store: false
|
|
71
|
-
parsed.store = false;
|
|
72
52
|
// Extract developer/system message as instructions
|
|
73
53
|
if (parsed.input && Array.isArray(parsed.input)) {
|
|
74
54
|
const devIdx = parsed.input.findIndex((m) => m.role === "developer" || m.role === "system");
|
|
@@ -79,19 +59,11 @@ export function createOAuthFetch(creds) {
|
|
|
79
59
|
}
|
|
80
60
|
}
|
|
81
61
|
body = JSON.stringify(parsed);
|
|
82
|
-
debug(`Transformed body: ${body.slice(0, 500)}`);
|
|
83
62
|
}
|
|
84
63
|
catch {
|
|
85
|
-
|
|
64
|
+
// ignore parse errors
|
|
86
65
|
}
|
|
87
66
|
}
|
|
88
|
-
|
|
89
|
-
// Debug: log response
|
|
90
|
-
debug(`Response status: ${response.status}`);
|
|
91
|
-
if (!response.ok) {
|
|
92
|
-
const text = await response.clone().text();
|
|
93
|
-
debug(`Error response: ${text.slice(0, 1000)}`);
|
|
94
|
-
}
|
|
95
|
-
return response;
|
|
67
|
+
return fetch(CODEX_ENDPOINT, { ...init, headers, body });
|
|
96
68
|
};
|
|
97
69
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { anthropic as _anthropic } from "@ai-sdk/anthropic";
|
|
2
2
|
import { AISDKLanguageModel } from "../language-model.js";
|
|
3
|
-
import type { OAuthCredentials } from "../oauth/types.js";
|
|
4
3
|
/**
|
|
5
4
|
* Anthropic model IDs (derived from @ai-sdk/anthropic).
|
|
6
5
|
*/
|
|
@@ -9,10 +8,8 @@ export type AnthropicModelId = Parameters<typeof _anthropic>[0];
|
|
|
9
8
|
* Options for creating a custom Anthropic provider.
|
|
10
9
|
*/
|
|
11
10
|
export interface AnthropicProviderOptions {
|
|
12
|
-
/** API key for
|
|
11
|
+
/** API key for authentication */
|
|
13
12
|
apiKey?: string;
|
|
14
|
-
/** OAuth credentials for Claude Pro/Max authentication */
|
|
15
|
-
oauth?: OAuthCredentials;
|
|
16
13
|
/** Custom base URL */
|
|
17
14
|
baseURL?: string;
|
|
18
15
|
/** Custom headers */
|
|
@@ -21,24 +18,11 @@ export interface AnthropicProviderOptions {
|
|
|
21
18
|
/**
|
|
22
19
|
* Create a custom Anthropic provider with explicit credentials.
|
|
23
20
|
*
|
|
24
|
-
* @example
|
|
21
|
+
* @example
|
|
25
22
|
* ```ts
|
|
26
23
|
* const anthropic = createAnthropic({ apiKey: "sk-..." });
|
|
27
24
|
* const model = anthropic("claude-sonnet-4-5");
|
|
28
25
|
* ```
|
|
29
|
-
*
|
|
30
|
-
* @example OAuth auth (Claude Pro/Max)
|
|
31
|
-
* ```ts
|
|
32
|
-
* const anthropic = createAnthropic({
|
|
33
|
-
* oauth: {
|
|
34
|
-
* accessToken: "...",
|
|
35
|
-
* refreshToken: "...",
|
|
36
|
-
* expiresAt: Date.now() + 3600000,
|
|
37
|
-
* onRefresh: (tokens) => saveTokens(tokens),
|
|
38
|
-
* }
|
|
39
|
-
* });
|
|
40
|
-
* const model = anthropic("claude-sonnet-4-5");
|
|
41
|
-
* ```
|
|
42
26
|
*/
|
|
43
27
|
export declare function createAnthropic(options?: AnthropicProviderOptions): (modelId: AnthropicModelId) => AISDKLanguageModel;
|
|
44
28
|
/**
|
|
@@ -54,5 +38,4 @@ export declare function createAnthropic(options?: AnthropicProviderOptions): (mo
|
|
|
54
38
|
* ```
|
|
55
39
|
*/
|
|
56
40
|
export declare function anthropic(modelId: AnthropicModelId): AISDKLanguageModel;
|
|
57
|
-
export type { OAuthCredentials } from "../oauth/types.js";
|
|
58
41
|
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,IAAI,UAAU,EAExB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,IAAI,UAAU,EAExB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;AAEhE;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,iCAAiC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sBAAsB;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,OAAO,GAAE,wBAA6B,IAO5D,SAAS,gBAAgB,wBAElC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,sBAGlD"}
|
|
@@ -1,34 +1,19 @@
|
|
|
1
1
|
import { anthropic as _anthropic, createAnthropic as _createAnthropic, } from "@ai-sdk/anthropic";
|
|
2
2
|
import { AISDKLanguageModel } from "../language-model.js";
|
|
3
|
-
import { createOAuthFetch } from "../oauth/anthropic.js";
|
|
4
3
|
/**
|
|
5
4
|
* Create a custom Anthropic provider with explicit credentials.
|
|
6
5
|
*
|
|
7
|
-
* @example
|
|
6
|
+
* @example
|
|
8
7
|
* ```ts
|
|
9
8
|
* const anthropic = createAnthropic({ apiKey: "sk-..." });
|
|
10
9
|
* const model = anthropic("claude-sonnet-4-5");
|
|
11
10
|
* ```
|
|
12
|
-
*
|
|
13
|
-
* @example OAuth auth (Claude Pro/Max)
|
|
14
|
-
* ```ts
|
|
15
|
-
* const anthropic = createAnthropic({
|
|
16
|
-
* oauth: {
|
|
17
|
-
* accessToken: "...",
|
|
18
|
-
* refreshToken: "...",
|
|
19
|
-
* expiresAt: Date.now() + 3600000,
|
|
20
|
-
* onRefresh: (tokens) => saveTokens(tokens),
|
|
21
|
-
* }
|
|
22
|
-
* });
|
|
23
|
-
* const model = anthropic("claude-sonnet-4-5");
|
|
24
|
-
* ```
|
|
25
11
|
*/
|
|
26
12
|
export function createAnthropic(options = {}) {
|
|
27
13
|
const provider = _createAnthropic({
|
|
28
|
-
apiKey: options.
|
|
14
|
+
apiKey: options.apiKey,
|
|
29
15
|
baseURL: options.baseURL,
|
|
30
16
|
headers: options.headers,
|
|
31
|
-
fetch: options.oauth ? createOAuthFetch(options.oauth) : undefined,
|
|
32
17
|
});
|
|
33
18
|
return (modelId) => new AISDKLanguageModel(provider(modelId));
|
|
34
19
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,IAAI,OAAO,EAElB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE7D;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,qBAA0B,
|
|
1
|
+
{"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,MAAM,IAAI,OAAO,EAElB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAGvD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAE7D;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;AAE1D;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oEAAoE;IACpE,KAAK,CAAC,EAAE,sBAAsB,CAAC;IAC/B,gEAAgE;IAChE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,qBAA0B,IAatD,SAAS,aAAa,wBAE/B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,aAAa,sBAG5C;AAGD,YAAY,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/providers/openai.js
CHANGED
|
@@ -33,7 +33,11 @@ export function createOpenAI(options = {}) {
|
|
|
33
33
|
headers: options.headers,
|
|
34
34
|
fetch: options.oauth ? createOAuthFetch(options.oauth) : undefined,
|
|
35
35
|
});
|
|
36
|
-
|
|
36
|
+
// OAuth requires store: false - Codex endpoint doesn't persist items
|
|
37
|
+
const settings = options.oauth
|
|
38
|
+
? { providerOptions: { openai: { store: false } } }
|
|
39
|
+
: undefined;
|
|
40
|
+
return (modelId) => new AISDKLanguageModel(provider(modelId), settings);
|
|
37
41
|
}
|
|
38
42
|
/**
|
|
39
43
|
* Create a kernl-compatible OpenAI language model.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kernl-sdk/ai",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.6",
|
|
4
4
|
"description": "Vercel AI SDK adapter for kernl",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"kernl",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"@kernl-sdk/protocol": "0.5.1",
|
|
72
72
|
"@kernl-sdk/retrieval": "0.1.10",
|
|
73
73
|
"@kernl-sdk/shared": "^0.4.0",
|
|
74
|
-
"kernl": "0.12.
|
|
74
|
+
"kernl": "0.12.4"
|
|
75
75
|
},
|
|
76
76
|
"scripts": {
|
|
77
77
|
"build": "tsc && tsc-alias --resolve-full-paths",
|
package/src/language-model.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { LanguageModelV3 } from "@ai-sdk/provider";
|
|
|
3
3
|
import type {
|
|
4
4
|
LanguageModel,
|
|
5
5
|
LanguageModelRequest,
|
|
6
|
+
LanguageModelRequestSettings,
|
|
6
7
|
LanguageModelResponse,
|
|
7
8
|
LanguageModelStreamEvent,
|
|
8
9
|
} from "@kernl-sdk/protocol";
|
|
@@ -23,7 +24,14 @@ export class AISDKLanguageModel implements LanguageModel {
|
|
|
23
24
|
readonly provider: string;
|
|
24
25
|
readonly modelId: string;
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
/**
|
|
28
|
+
* @param model - The underlying AI SDK model
|
|
29
|
+
* @param settings - Default settings to apply to every request (overridden by per-request settings)
|
|
30
|
+
*/
|
|
31
|
+
constructor(
|
|
32
|
+
private model: LanguageModelV3,
|
|
33
|
+
private settings?: Partial<LanguageModelRequestSettings>,
|
|
34
|
+
) {
|
|
27
35
|
this.provider = normalizeProvider(model.provider);
|
|
28
36
|
this.modelId = model.modelId;
|
|
29
37
|
}
|
|
@@ -36,15 +44,16 @@ export class AISDKLanguageModel implements LanguageModel {
|
|
|
36
44
|
): Promise<LanguageModelResponse> {
|
|
37
45
|
const messages = request.input.map(MESSAGE.encode);
|
|
38
46
|
const tools = request.tools ? request.tools.map(TOOL.encode) : undefined;
|
|
39
|
-
const
|
|
47
|
+
const merged = { ...this.settings, ...request.settings };
|
|
48
|
+
const settings = MODEL_SETTINGS.encode(merged);
|
|
40
49
|
const responseFormat = RESPONSE_FORMAT.encode(request.responseType);
|
|
41
50
|
|
|
42
51
|
const result = await this.model.doGenerate({
|
|
43
52
|
prompt: messages,
|
|
44
53
|
tools,
|
|
45
|
-
...settings,
|
|
46
54
|
responseFormat,
|
|
47
55
|
abortSignal: request.abort,
|
|
56
|
+
...settings,
|
|
48
57
|
});
|
|
49
58
|
|
|
50
59
|
return MODEL_RESPONSE.decode(result);
|
|
@@ -58,15 +67,16 @@ export class AISDKLanguageModel implements LanguageModel {
|
|
|
58
67
|
): AsyncIterable<LanguageModelStreamEvent> {
|
|
59
68
|
const messages = request.input.map(MESSAGE.encode);
|
|
60
69
|
const tools = request.tools ? request.tools.map(TOOL.encode) : undefined;
|
|
61
|
-
const
|
|
70
|
+
const merged = { ...this.settings, ...request.settings };
|
|
71
|
+
const settings = MODEL_SETTINGS.encode(merged);
|
|
62
72
|
const responseFormat = RESPONSE_FORMAT.encode(request.responseType);
|
|
63
73
|
|
|
64
74
|
const stream = await this.model.doStream({
|
|
65
75
|
prompt: messages,
|
|
66
76
|
tools,
|
|
67
|
-
...settings,
|
|
68
77
|
responseFormat,
|
|
69
78
|
abortSignal: request.abort,
|
|
79
|
+
...settings,
|
|
70
80
|
});
|
|
71
81
|
|
|
72
82
|
// text + reasoning buffers for delta accumulation
|
package/src/oauth/openai.ts
CHANGED
|
@@ -1,21 +1,9 @@
|
|
|
1
|
-
import { appendFileSync } from "node:fs";
|
|
2
1
|
import type { OpenAIOAuthCredentials } from "./types";
|
|
3
2
|
|
|
4
3
|
const TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
5
4
|
const CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
6
5
|
const CODEX_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
|
|
7
6
|
|
|
8
|
-
function debug(msg: string) {
|
|
9
|
-
try {
|
|
10
|
-
appendFileSync(
|
|
11
|
-
"/tmp/popcorn-debug.log",
|
|
12
|
-
`${new Date().toISOString()} [oauth/fetch] ${msg}\n`,
|
|
13
|
-
);
|
|
14
|
-
} catch {
|
|
15
|
-
// ignore
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
7
|
interface TokenResponse {
|
|
20
8
|
access_token: string;
|
|
21
9
|
refresh_token: string;
|
|
@@ -75,31 +63,18 @@ export function createOAuthFetch(creds: OpenAIOAuthCredentials) {
|
|
|
75
63
|
headers.set("ChatGPT-Account-Id", creds.accountId);
|
|
76
64
|
}
|
|
77
65
|
|
|
78
|
-
// Debug: log request
|
|
79
|
-
const url =
|
|
80
|
-
typeof input === "string"
|
|
81
|
-
? input
|
|
82
|
-
: input instanceof URL
|
|
83
|
-
? input.toString()
|
|
84
|
-
: input.url;
|
|
85
|
-
debug(`Request to: ${url}`);
|
|
86
|
-
debug(`Redirecting to: ${CODEX_ENDPOINT}`);
|
|
87
|
-
|
|
88
66
|
// Transform request body for Codex API
|
|
89
67
|
// Codex requires "instructions" field instead of developer/system role in input
|
|
90
|
-
// Codex also requires "store: false"
|
|
91
68
|
let body = init?.body;
|
|
92
69
|
if (body && typeof body === "string") {
|
|
93
70
|
try {
|
|
94
71
|
const parsed = JSON.parse(body);
|
|
95
72
|
|
|
96
|
-
// Codex requires store: false
|
|
97
|
-
parsed.store = false;
|
|
98
|
-
|
|
99
73
|
// Extract developer/system message as instructions
|
|
100
74
|
if (parsed.input && Array.isArray(parsed.input)) {
|
|
101
75
|
const devIdx = parsed.input.findIndex(
|
|
102
|
-
(m:
|
|
76
|
+
(m: Record<string, unknown>) =>
|
|
77
|
+
m.role === "developer" || m.role === "system",
|
|
103
78
|
);
|
|
104
79
|
if (devIdx !== -1) {
|
|
105
80
|
const devMsg = parsed.input[devIdx];
|
|
@@ -109,21 +84,11 @@ export function createOAuthFetch(creds: OpenAIOAuthCredentials) {
|
|
|
109
84
|
}
|
|
110
85
|
|
|
111
86
|
body = JSON.stringify(parsed);
|
|
112
|
-
debug(`Transformed body: ${body.slice(0, 500)}`);
|
|
113
87
|
} catch {
|
|
114
|
-
|
|
88
|
+
// ignore parse errors
|
|
115
89
|
}
|
|
116
90
|
}
|
|
117
91
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
// Debug: log response
|
|
121
|
-
debug(`Response status: ${response.status}`);
|
|
122
|
-
if (!response.ok) {
|
|
123
|
-
const text = await response.clone().text();
|
|
124
|
-
debug(`Error response: ${text.slice(0, 1000)}`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return response;
|
|
92
|
+
return fetch(CODEX_ENDPOINT, { ...init, headers, body });
|
|
128
93
|
};
|
|
129
94
|
}
|
|
@@ -3,8 +3,6 @@ import {
|
|
|
3
3
|
createAnthropic as _createAnthropic,
|
|
4
4
|
} from "@ai-sdk/anthropic";
|
|
5
5
|
import { AISDKLanguageModel } from "../language-model";
|
|
6
|
-
import { createOAuthFetch } from "../oauth/anthropic";
|
|
7
|
-
import type { OAuthCredentials } from "../oauth/types";
|
|
8
6
|
|
|
9
7
|
/**
|
|
10
8
|
* Anthropic model IDs (derived from @ai-sdk/anthropic).
|
|
@@ -15,10 +13,8 @@ export type AnthropicModelId = Parameters<typeof _anthropic>[0];
|
|
|
15
13
|
* Options for creating a custom Anthropic provider.
|
|
16
14
|
*/
|
|
17
15
|
export interface AnthropicProviderOptions {
|
|
18
|
-
/** API key for
|
|
16
|
+
/** API key for authentication */
|
|
19
17
|
apiKey?: string;
|
|
20
|
-
/** OAuth credentials for Claude Pro/Max authentication */
|
|
21
|
-
oauth?: OAuthCredentials;
|
|
22
18
|
/** Custom base URL */
|
|
23
19
|
baseURL?: string;
|
|
24
20
|
/** Custom headers */
|
|
@@ -28,31 +24,17 @@ export interface AnthropicProviderOptions {
|
|
|
28
24
|
/**
|
|
29
25
|
* Create a custom Anthropic provider with explicit credentials.
|
|
30
26
|
*
|
|
31
|
-
* @example
|
|
27
|
+
* @example
|
|
32
28
|
* ```ts
|
|
33
29
|
* const anthropic = createAnthropic({ apiKey: "sk-..." });
|
|
34
30
|
* const model = anthropic("claude-sonnet-4-5");
|
|
35
31
|
* ```
|
|
36
|
-
*
|
|
37
|
-
* @example OAuth auth (Claude Pro/Max)
|
|
38
|
-
* ```ts
|
|
39
|
-
* const anthropic = createAnthropic({
|
|
40
|
-
* oauth: {
|
|
41
|
-
* accessToken: "...",
|
|
42
|
-
* refreshToken: "...",
|
|
43
|
-
* expiresAt: Date.now() + 3600000,
|
|
44
|
-
* onRefresh: (tokens) => saveTokens(tokens),
|
|
45
|
-
* }
|
|
46
|
-
* });
|
|
47
|
-
* const model = anthropic("claude-sonnet-4-5");
|
|
48
|
-
* ```
|
|
49
32
|
*/
|
|
50
33
|
export function createAnthropic(options: AnthropicProviderOptions = {}) {
|
|
51
34
|
const provider = _createAnthropic({
|
|
52
|
-
apiKey: options.
|
|
35
|
+
apiKey: options.apiKey,
|
|
53
36
|
baseURL: options.baseURL,
|
|
54
37
|
headers: options.headers,
|
|
55
|
-
fetch: options.oauth ? createOAuthFetch(options.oauth) : undefined,
|
|
56
38
|
});
|
|
57
39
|
|
|
58
40
|
return (modelId: AnthropicModelId) =>
|
|
@@ -76,7 +58,4 @@ export function anthropic(modelId: AnthropicModelId) {
|
|
|
76
58
|
return new AISDKLanguageModel(model);
|
|
77
59
|
}
|
|
78
60
|
|
|
79
|
-
// Re-export types
|
|
80
|
-
export type { OAuthCredentials } from "../oauth/types";
|
|
81
|
-
|
|
82
61
|
// Note: Anthropic does not currently support embeddings
|
package/src/providers/openai.ts
CHANGED
|
@@ -59,7 +59,13 @@ export function createOpenAI(options: OpenAIProviderOptions = {}) {
|
|
|
59
59
|
fetch: options.oauth ? createOAuthFetch(options.oauth) : undefined,
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
// OAuth requires store: false - Codex endpoint doesn't persist items
|
|
63
|
+
const settings = options.oauth
|
|
64
|
+
? { providerOptions: { openai: { store: false } } }
|
|
65
|
+
: undefined;
|
|
66
|
+
|
|
67
|
+
return (modelId: OpenAIModelId) =>
|
|
68
|
+
new AISDKLanguageModel(provider(modelId), settings);
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
/**
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { OAuthCredentials } from "./types.js";
|
|
2
|
-
/**
|
|
3
|
-
* Create a fetch wrapper for Anthropic OAuth.
|
|
4
|
-
*
|
|
5
|
-
* Uses the standard Anthropic API with OAuth bearer token.
|
|
6
|
-
*/
|
|
7
|
-
export declare function createOAuthFetch(creds: OAuthCredentials): (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
8
|
-
//# sourceMappingURL=anthropic.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/oauth/anthropic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAoDhD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,IAEpD,OAAO,MAAM,GAAG,GAAG,GAAG,OAAO,EAC7B,OAAO,WAAW,KACjB,OAAO,CAAC,QAAQ,CAAC,CAwBrB"}
|
package/dist/oauth/anthropic.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { appendFileSync } from "node:fs";
|
|
2
|
-
const TOKEN_URL = "https://console.anthropic.com/v1/oauth/token";
|
|
3
|
-
const CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
4
|
-
function debug(msg) {
|
|
5
|
-
try {
|
|
6
|
-
appendFileSync("/tmp/popcorn-debug.log", `${new Date().toISOString()} [oauth/anthropic] ${msg}\n`);
|
|
7
|
-
}
|
|
8
|
-
catch {
|
|
9
|
-
// ignore
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Refresh Anthropic OAuth tokens.
|
|
14
|
-
*/
|
|
15
|
-
async function refresh(creds) {
|
|
16
|
-
debug(`Refreshing token...`);
|
|
17
|
-
const res = await fetch(TOKEN_URL, {
|
|
18
|
-
method: "POST",
|
|
19
|
-
headers: { "Content-Type": "application/json" },
|
|
20
|
-
body: JSON.stringify({
|
|
21
|
-
grant_type: "refresh_token",
|
|
22
|
-
refresh_token: creds.refreshToken,
|
|
23
|
-
client_id: CLIENT_ID,
|
|
24
|
-
}),
|
|
25
|
-
});
|
|
26
|
-
if (!res.ok) {
|
|
27
|
-
throw new Error(`Token refresh failed: ${res.status}`);
|
|
28
|
-
}
|
|
29
|
-
const data = (await res.json());
|
|
30
|
-
creds.accessToken = data.access_token;
|
|
31
|
-
creds.refreshToken = data.refresh_token;
|
|
32
|
-
creds.expiresAt = Date.now() + data.expires_in * 1000;
|
|
33
|
-
creds.onRefresh?.({
|
|
34
|
-
accessToken: creds.accessToken,
|
|
35
|
-
refreshToken: creds.refreshToken,
|
|
36
|
-
expiresAt: creds.expiresAt,
|
|
37
|
-
});
|
|
38
|
-
debug(`Token refreshed successfully`);
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Create a fetch wrapper for Anthropic OAuth.
|
|
42
|
-
*
|
|
43
|
-
* Uses the standard Anthropic API with OAuth bearer token.
|
|
44
|
-
*/
|
|
45
|
-
export function createOAuthFetch(creds) {
|
|
46
|
-
return async (input, init) => {
|
|
47
|
-
// Refresh if expired (with 30s buffer)
|
|
48
|
-
if (Date.now() >= creds.expiresAt - 30_000) {
|
|
49
|
-
await refresh(creds);
|
|
50
|
-
}
|
|
51
|
-
const headers = new Headers(init?.headers);
|
|
52
|
-
headers.set("Authorization", `Bearer ${creds.accessToken}`);
|
|
53
|
-
// Required beta header for OAuth
|
|
54
|
-
headers.set("anthropic-beta", "oauth-2025-04-20");
|
|
55
|
-
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
56
|
-
debug(`Request to: ${url}`);
|
|
57
|
-
const response = await fetch(input, { ...init, headers });
|
|
58
|
-
debug(`Response status: ${response.status}`);
|
|
59
|
-
if (!response.ok) {
|
|
60
|
-
const text = await response.clone().text();
|
|
61
|
-
debug(`Error response: ${text.slice(0, 1000)}`);
|
|
62
|
-
}
|
|
63
|
-
return response;
|
|
64
|
-
};
|
|
65
|
-
}
|
package/src/oauth/anthropic.ts
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { appendFileSync } from "node:fs";
|
|
2
|
-
import type { OAuthCredentials } from "./types";
|
|
3
|
-
|
|
4
|
-
const TOKEN_URL = "https://console.anthropic.com/v1/oauth/token";
|
|
5
|
-
const CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
6
|
-
|
|
7
|
-
function debug(msg: string) {
|
|
8
|
-
try {
|
|
9
|
-
appendFileSync("/tmp/popcorn-debug.log", `${new Date().toISOString()} [oauth/anthropic] ${msg}\n`);
|
|
10
|
-
} catch {
|
|
11
|
-
// ignore
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
interface TokenResponse {
|
|
16
|
-
access_token: string;
|
|
17
|
-
refresh_token: string;
|
|
18
|
-
expires_in: number;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Refresh Anthropic OAuth tokens.
|
|
23
|
-
*/
|
|
24
|
-
async function refresh(creds: OAuthCredentials): Promise<void> {
|
|
25
|
-
debug(`Refreshing token...`);
|
|
26
|
-
const res = await fetch(TOKEN_URL, {
|
|
27
|
-
method: "POST",
|
|
28
|
-
headers: { "Content-Type": "application/json" },
|
|
29
|
-
body: JSON.stringify({
|
|
30
|
-
grant_type: "refresh_token",
|
|
31
|
-
refresh_token: creds.refreshToken,
|
|
32
|
-
client_id: CLIENT_ID,
|
|
33
|
-
}),
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
if (!res.ok) {
|
|
37
|
-
throw new Error(`Token refresh failed: ${res.status}`);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const data = (await res.json()) as TokenResponse;
|
|
41
|
-
|
|
42
|
-
creds.accessToken = data.access_token;
|
|
43
|
-
creds.refreshToken = data.refresh_token;
|
|
44
|
-
creds.expiresAt = Date.now() + data.expires_in * 1000;
|
|
45
|
-
|
|
46
|
-
creds.onRefresh?.({
|
|
47
|
-
accessToken: creds.accessToken,
|
|
48
|
-
refreshToken: creds.refreshToken,
|
|
49
|
-
expiresAt: creds.expiresAt,
|
|
50
|
-
});
|
|
51
|
-
debug(`Token refreshed successfully`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Create a fetch wrapper for Anthropic OAuth.
|
|
56
|
-
*
|
|
57
|
-
* Uses the standard Anthropic API with OAuth bearer token.
|
|
58
|
-
*/
|
|
59
|
-
export function createOAuthFetch(creds: OAuthCredentials) {
|
|
60
|
-
return async (
|
|
61
|
-
input: string | URL | Request,
|
|
62
|
-
init?: RequestInit,
|
|
63
|
-
): Promise<Response> => {
|
|
64
|
-
// Refresh if expired (with 30s buffer)
|
|
65
|
-
if (Date.now() >= creds.expiresAt - 30_000) {
|
|
66
|
-
await refresh(creds);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const headers = new Headers(init?.headers);
|
|
70
|
-
headers.set("Authorization", `Bearer ${creds.accessToken}`);
|
|
71
|
-
// Required beta header for OAuth
|
|
72
|
-
headers.set("anthropic-beta", "oauth-2025-04-20");
|
|
73
|
-
|
|
74
|
-
const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
|
|
75
|
-
debug(`Request to: ${url}`);
|
|
76
|
-
|
|
77
|
-
const response = await fetch(input, { ...init, headers });
|
|
78
|
-
|
|
79
|
-
debug(`Response status: ${response.status}`);
|
|
80
|
-
if (!response.ok) {
|
|
81
|
-
const text = await response.clone().text();
|
|
82
|
-
debug(`Error response: ${text.slice(0, 1000)}`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return response;
|
|
86
|
-
};
|
|
87
|
-
}
|